آموزش اندروید، فصل ۱۹: Activity (بخش ۲) – اپلیکیشن پخش موسیقی

در قسمت قبلی از سری مباجث آموزش برنامه نویسی اندروید، کار را با Activity ها شروع کردیم (اینجا).همچنین درباره چرخه زندگی Activity ها صحبت کردیم و امروز میخواهیم در قالب یک پروژه عملی و کاربردی آن را بررسی کنیم. پروژه ای که انتخاب کردیم یک نرم افزار پخش آهنگ ساده و زیبا (Music Player)است که هم کدهای جاوای آن را بررسی می کنیم و هم طراحی layout آن را. پس با ما همراه باشید تا قدم به قدم با یکدیگر پیش برویم.

نویسنده: احسان قربان نژاد

لینک دانلود سورس کامل پروژه: اینجا

برای شروع سورس کد کامل پروژه را از لینک بالا میتوانید دریافت کنید. ما فرض میکنیم می خواهید از صفر شروع کنید. برای این کار، ابتدا یک پروژه اندروید بسازید (در مباحث قبلی به آن پرداخته شده است) . پروژه ما در محیط Eclipse نوشته شده است. شما میتوانید در Android Studio آن را بنویسید چرا که دستورات همان دستورات هستند.

شروع طراحی

می دانیم که با ساخت یک پروژه دو فایل MainActiviyty.java و activity_main.xml ایجاد میگردند (یکی در پوشه src و دیگری در پوشه res/layout ) برای شروع به سراغ آرایش صفحه موزیک پلیرمان می رویم (یعنی activity_main.xml ) . صفحه ای که می خواهیم طراحی کنیم باید به شکل زیر در آید. برای شروع کدهای زیر را در فایل activity_main.xml کپی کنید)

activity_main.xmli

activity_main.xmli

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="10dp" >
 
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" >
 
        <ImageView
            android:id="@+id/songImage"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#eee"
            android:scaleType="centerCrop" />
 
        <ImageButton
            android:id="@+id/playBtn"
            android:layout_width="160dp"
            android:layout_height="160dp"
            android:layout_centerInParent="true"
            android:background="@drawable/play_selector"
            android:tag="pause" />
 
        <TextView
            android:id="@+id/title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#2d2d2d"
            android:gravity="center"
            android:padding="10dp"
            android:textColor="#fff"
            android:textSize="22sp"
            android:textStyle="bold" />
    </RelativeLayout>
 
    <SeekBar
        android:id="@+id/seekBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:progressDrawable="@drawable/red_scrubber_progress"
        android:thumb="@drawable/normal_scrobler" />
 
</LinearLayout>

در آرایش صفحه ما از SeekBar برای کنترل پخش موسیقی، ImageView برای نمایش تصویر کاور آهنگ، ImageButton برای دکمه play وTextView برای نمایش عنوان ترانه استفاده کرده ایم. دقت کنید که سه تگ عکس، دکمه و تکست، هر سه در لایه ای از نوع RelativeLayout قرار گرفته اند تا بتوانیم آنها را روی هم بچینیم. همچنین این RelativeLayout را به همراه SeekBar در لایه ای از نوع LinearLayout قرار داده ایم تا زیر هم با یک نسبت قرار گیرند. ( به صفت layout_weight در LinearLayout دقت کنید. دقت کنید که ما درطراحی این layout از یکسری drawable استفاده کرده ایم و آن هارا با دستور @drawable آدرس داده ایم. بعضی از آنها عکس با فرمت .png هستند و بعضی از آنها فایل با پسوند xml. که در پوشه ای که خودمان با نام drawable ساخته ایم، قرار دارد. کل این پوشه را از داخل سورس کامل پروژه کپی کنید و به پوشه res پروژه خودتان اضافه کنید.  این پوشه حاوی چندین تصویر و همچنین ۳ فایل xml است که این سه فایل در زیر آورده شده اند:

play_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:state_pressed="true" android:drawable="@drawable/play_press" />
    <item android:drawable="@drawable/play" />
</selector>

pause_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:state_pressed="true" android:drawable="@drawable/pause_press" />
    <item android:drawable="@drawable/pause" />
</selector>

ما برای دکمه play و pause یک ImageButton مد نظر قرار داده ایم و برای background آن بجای آدرس به یک drawable از نوع عکس، یک selector در نظر گرفته ایم. در سلکتور ها رفتار کلیک را میتوانیم توضیف کنیم. سلکتور ها میگویند که در هنگام press شدن یک View چه تغییر شکلی رو آن اعمال شود. مثلا ما در این پروژه میخواهیم وقتی کاربر دکمه را لمس کرد، قبل از برداشته شدن انگشتش، عکس آیکن مان تغییر رنگ دهد. به همین منظور بود که در بالا از هر آیکن دو نوع با رنگهای متفاوت قرار دادیم. ساختار فایل های selector را دربالا میینید. علاوه بر selector، نیاز داریم تا شکل seekBar را به شیوه ای که خودمان دوست داریم طراحی کنیم. این کار را، هم برای شستی (thumb) و هم برای نوار پس زمینه seekBar انجام داده ایم که در زیر آن را می بینید. برای درگیر نشدن با جزییات طراحی، کافیست پوشه drawable را از سورس کامل پروژه به داخل برنامه تان کپی کنید. تمرکز ما در این آموزش در برنامه نویسی در داخل MainActivity.java است.

red_scrubber_progress.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
 
    <item
        android:id="@android:id/background"
        android:drawable="@drawable/red_scrubber_track_holo_light"/>
    <item android:id="@android:id/secondaryProgress">
        <scale
            android:drawable="@drawable/red_scrubber_secondary_holo"
            android:scaleWidth="100%" />
    </item>
    <item android:id="@android:id/progress">
        <scale
            android:drawable="@drawable/red_scrubber_primary_holo"
            android:scaleWidth="100%" />
    </item>
 
</layer-list>

MainActivit.java

حال که طراحی مان به اتمام رسید، وقت آن است المان های آن را در سمت جاوا دستکاری کنیم. کد کامل کلاس MainActivity.java در انتهای پست قرار داده شده است. ما میخواهیم این کلاس راT مرحله به مرحله بسازیم و کامل کنیم. فایل MainActivity.java پروژه تان، را باز کنید. کارمان را با متد OnCreate شروع میکنیم. اما قبل از رفتن به داخل onCreate المان های مورد نیاز را قبل از آن (در بدنه کلاس)، بصورت زیر تعریف کنید:

package ir.ehsanet.sample.musicplayer;

import android.app.Activity;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;

public class MainActivity extends Activity {

    ImageButton imgBtn;
    TextView titleTextView;
    MediaPlayer mp;
    SeekBar seekbar;
    Handler handler;
    Runnable runnable;
    ImageView songImageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ...
    }
}

حال باید در اولین قدم، در داخل onCreate المان ها را find کنیم.

super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imgBtn = (ImageButton) findViewById(R.id.playBtn);
seekbar = (SeekBar) findViewById(R.id.seekBar);
songImageView = (ImageView) findViewById(R.id.songImage);
titleTextView = (TextView) findViewById(R.id.title);

سپس عنوان و تصویر آهنگ را set میکنیم. برای اینکار از دستورات زیر استفاده میکنیم:

titleTextView.setText("Ashoobam - Chartar");
songImageView.setImageResource(R.drawable.song_image);

ساخت یک MediaPlayer

حال نوبت تعیین آهنگی که باید پخش شود می رسد. برای اینکار از کلاس MediaPlayer استفاده میکنیم و متد Create را روی آن صدا میزنیم. این متد در خروجی خود، یک شیء از نوع MediaPlayer به ما میدهد که کافیست متد start() را روی آن صدا بزنیم. قبل از آن یادمان باشد باید یک آهنگ در پوشه res/raw قرار دهیم. اگر پوشه raw را ندارید آن را بسازید و یک فایل mp3داخل آن قرار دهید. دقت کنید که اسم فایل تان تنها از حروف کوچک، آندرلاین و اعداد تشکیل شده باشد . حروف بزرگ یا فاصله و یا سایر علامت ها کار را خراب میکند. حال می توانید در جاوا همانطور که در جلسه قبلی بیان شد، آهنگتان را با R.raw.file_name آدرسدهی کنید. یادمان باشد بطور کلی هرگاه بخواهیم چیزی را از پوشه res در جاوا آدرس دهی کنیم از R استفاده کنیم. گفتیم که برای پخش یک موسیقی یا صدا در اندروید باید از کلاس MediaPlaer استفاده کنیم. برای اینکار از کدهای زیر استفاده مینکیم.

mp = MediaPlayer.create(MainActivity.this, R.raw.music);

نکته) ما در این پروژه آهنگ را از پوشه اپ خودمان یعنی raw می خوانیم. اگر میخواهید آهنگ ها را از روی حافظه sdcard بخوانید کمی ساخت MediaPlayer تان تفاوت خواهد کرد که در آین آموزش به آن پرداخته نمی شود.

بروزرسانی موقعیت SeekBar

ما میخواهیم SeekBar را در حین پخش آهنگ، آپدیت کنیم تا همراه آهنگ جلو برود.  برای این کار از کلاس Handler استفاده میکنیم. یکی از کارهایی که با handler ها میتوان کرد، انجام کاری پس از چند ثانیه است. ما فعلا یک handler و یک کار از نوع Runnable تعریف کرده ایم.

handler = new Handler();
runnable = new Runnable() {
    @Override
    public void run() {
        updateSeekbar();
    }
};

در داخل متد run از Runnable متد updateSeekBar را صدا زده ایم. این متد را که بصورت زیر است، را در خارج از OnCreate ( والبته در داخل کلاسMainActivty اضافه کنید)

public void updateSeekbar() {
    //find current progress position of mediaplayer
    float progress = ((float) mp.getCurrentPosition() / mp.getDuration());
    //set this progress to seekbar
    seekbar.setProgress((int) (progress * 100));
    //run handler again after 1 second
    handler.postDelayed(runnable, 1000);
}

در این متد ابتدا پیشرفت آهنگ مجاسبه میشود، سپس بر روی SeekBar مقدار آن set می شود. دقت کنید که در پایان این متد با دستور handler.postDelayed(runnable,1000) دوباره خودش،یعنی updateSeekbar ، صدا زده زده میشود اما با تاخیر ۱۰۰۰ میلی ثانیه ای یعنی یک ثانیه.اگر کمی به آن فکر کنید متوجه علت کار خواهید شد. دلیل آن است که SeekBar هر یک ثانیه باید آپدیت شود (جلو برود با آهنگ) و ما مسئول آپدیت کردن آن هستیم. پس چه بهتر که هر یک ثانیه خودش را صدا بزنیم.

اما فرآیند pause و play کردن به چه صورت است؟

تا اینجا ما فقط همه چیز را تعریف کردیم. برای این اینکه آهنگ شروع به پخش شود باید روی شی ای که از کلاس MediaPlayer ساختیم ( به نام mp ) ، متد start را صدا بزنیم. اما این کار را وقتی میکنیم که بدانیم روی دکمه play کلیک شده است. اما Pause کردن چی؟ ما در دو زمان، آهنگ را باید pause میکنیم. یکی هنگامی که کاربر روی دکمه play قبلی که حالا تبدیل به دکمه pause شده کلیک کند. و دیگری هنگامی که یک اتفاق مخصوصی که خارج از اپ ما بیوفتد. مثلا یک نفر با ما تماس برقرارکند (که آهنگ دیگر پخش نشود) و یا کاربر از اپ به هرصورتی خارج شود (بی آنکه روی دکمه Pause کلیک کند. مثلا از اپ ما بپرد به یک اپ دیگر). یادمان باشد در این دو مورد، با اینکه از اپ خارج میشویم، آهنگ قطع نمیشود و ما مسئول قطع کردن آن هستیم. برای اینکه بخواهیم روی click یک المان کاری انجام دهیم باید برای آن المان، onClickListener بنویسیم و با دستور setOnClickListener ، آن را به المان نسبت بدهیم. کدهای زیر را در داخل OnCrete و در انتهای آن اضافه میکنیم:

imgBtn.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        String s = (String) imgBtn.getTag();
        if (s.equals("pause")) {
            play();
        } else {
            pause();
        }
    }
});

(دقت کنید اگر کدها را مرجله به مرحله به پروژه تان اضافه می کنید، شاید زیر دستورات خطوط قرمزی ظاهر شود مبنی بر اینکه دستوراتی ناشناخته در حال استفاده است. برای حل این مشکل، هر قطعه کدی که از جای دیگر، به برنامه تان اضافه میکنید ، یک بار ctrl + shift + o را فشار دهید تا کلاس های لازم به برنامه تان Import شود.)

در بالا، چون ما تنها یک دکمه داریم ابتدا باید تشخیص دهیم که وضعیت دکمه در حال حاضر play است یا pause. برای این کار از دستورات setTag(…) و getTag() استفاده کرده ایم. از این دستورات برای نگهداری دیتا ( از هر نوعی) روی هر المانی استفاده میشود ( مثلا در اینجا المان ImageButton) در این دستورات ما از دو متد play() و pause() استفاده کردیم. دو متد را در خارج از OnCreate ( والبته در داخل کلاسMainActivty) اضافه کنید.

public void pause() {
    mp.pause();
    imgBtn.setTag("pause"); // next state should be 'pause'
    imgBtn.setBackgroundResource(R.drawable.play_selector);
    // stop seekbar
    handler.removeCallbacks(runnable);
}

public void play() {
    mp.start();
    imgBtn.setTag("play"); // next state should be 'pause'
    imgBtn.setBackgroundResource(R.drawable.pause_selector);
    //start seekbar
    updateSeekbar();
}

نکته: دستور handler.removeCallbacks(runnable) در داخل متد () pause ، چرخه فراخوانی updateSekkbar را می شکند و آن را متوقف میکند. برای دوباره راه انداختن آپدیت یک ثانیه ای مان کافیست () updateSekkbar را در () play صدا بزنیم. تا اینجا کار تمام است. اگر برنامه را تست کنید، به درستی کار میکند. اما ما هنوز فکری به حال Pause کردن اتوماتیک و بروز رسانی seekBar نکرده ایم.

حالت دوم pause

این حالت وقتی روی می دهد که کاربر یا دکمه back را بزند، یا به صفحه دیگری از اپ خودمان برود، یا یک نفر زنگ بزند، یا اپ دیگری روی اپ ما باز شود. در این جالت ما نیاز داریم درباره چرخه حیات Activity ها اطلاع داشته باشم که ما این موضوع را در مطلب قبلی از سری آموزش های اندروید بررسی کردیم. برای این هدف،(..) onPause را در داخل Activity مان بازنویسی میکنیم.(برای زمانی که کاربر ناخودآگاه یا خودآگاه از Activity خارج میشود) همچنین متد (..) onResume برای وقتی که کابر دوباره برمی گردد. دو متد زیر را به کلاس Activity تان اضافه کنید.

    @Override
    protected void onPause() {
        super.onPause();
        mp.pause();
        imgBtn.setBackgroundResource(R.drawable.play_selector);
    }

    @Override
    protected void onResume() {
        super.onResume();
        String s = (String) imgBtn.getTag();
        // check if its not first onResume which is invoked
        // note that onResum invoked always after onCreate()-> onStart()->
        // and also after we come back to our application (Activity)
        // if s equals to 'pause' it means we used setTag('pause') before
        // it means that we now should start  the player again.
        if (!s.equals("pause")) {
            mp.start();
            imgBtn.setBackgroundResource(R.drawable.pause_selector);
        }

    }

کنترل SeekBar

برای کنترل seekbar توسط کاربر (عقب جلو کشیدن شستی آن برای جابجایی موقعیت آهنگ) دستورات زیر را به داخل OnCreate در ادامه دستوراتی که قبلا گفتیم اضافه کنید.

seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        long now = (long) ((float) seekbar.getProgress() / 100 * mp.getDuration());
        mp.seekTo((int) now);
        handler.postDelayed(runnable, 1000);
    }
    
    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
        handler.removeCallbacks(runnable);
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress,boolean fromUser) {

    }
});

همچنین برای اینکه فرآیند آپدیت seekBar که هریک ثانیه اتفاق می افتاد و تغییر شکل آیکون pause به play در صورت رسیدن به آخر آهنگ اتفاق بیوفتد، دستور زیر را در ادامه قبلی ها، به داخل OnCreate اضافه کنید.

mp.setOnCompletionListener(new OnCompletionListener() {
    @Override
    public void onCompletion(MediaPlayer mp) {
        pause();
    }
});

فایل نهایی MainActivity.java

کلاس MainActivity ما آماده است. در زیر میتوانید همه کدهای آن را با هم مشاهده کنید.

package ir.ehsanet.sample.musicplayer;

import android.app.Activity;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;

public class MainActivity extends Activity {

    ImageButton imgBtn;
    TextView titleTextView;
    MediaPlayer mp;
    SeekBar seekbar;
    Handler handler;
    Runnable runnable;
    ImageView songImageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imgBtn = (ImageButton) findViewById(R.id.playBtn);
        seekbar = (SeekBar) findViewById(R.id.seekBar);
        songImageView = (ImageView) findViewById(R.id.songImage);
        titleTextView = (TextView) findViewById(R.id.title);
        
        titleTextView.setText("Ashoobam - Chartar");
        songImageView.setImageResource(R.drawable.song_image);
        mp = MediaPlayer.create(MainActivity.this, R.raw.music);

        handler = new Handler();
        runnable = new Runnable() {
            @Override
            public void run() {
                updateSeekbar();
            }
        };

        seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

                long now = (long) ((float) seekbar.getProgress() / 100 * mp
                        .getDuration());
                mp.seekTo((int) now);
                handler.postDelayed(runnable, 1000);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                handler.removeCallbacks(runnable);
            }

            @Override
            public void onProgressChanged(SeekBar seekBar, int progress,
                    boolean fromUser) {

            }
        });

        mp.setOnCompletionListener(new OnCompletionListener() {

            @Override
            public void onCompletion(MediaPlayer mp) {
                pause();
            }
        });

        imgBtn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                String s = (String) imgBtn.getTag();
                if (s.equals("pause")) {
                    play();
                } else {
                    pause();
                }
            }
        });

    }

    @Override
    protected void onPause() {
        super.onPause();
        mp.pause();
        imgBtn.setBackgroundResource(R.drawable.play_selector);
    }

    @Override
    protected void onResume() {
        super.onResume();
        String s = (String) imgBtn.getTag();
        // check if its not first onResume which is invoked
        // note that onResum invoked always after onCreate()-> onStart()->
        // and also after we come back to our application (Activity)
        // if s equals to 'pause' it means we used setTag('pause') before
        // it means that we now should start  the player again.
        if (!s.equals("pause")) {
            mp.start();
            imgBtn.setBackgroundResource(R.drawable.pause_selector);
        }

    }

    public void updateSeekbar() {
        //find current progress position of mediaplayer
        float progress = ((float) mp.getCurrentPosition() / mp.getDuration());
        //set this progress to seekbar
        seekbar.setProgress((int) (progress * 100));
        //run handler again after 1 second
        handler.postDelayed(runnable, 1000);
    }

    public void pause() {
        mp.pause();
        imgBtn.setTag("pause"); // next state should be 'pause'
        imgBtn.setBackgroundResource(R.drawable.play_selector);
        // stop seekbar
        handler.removeCallbacks(runnable);
    }

    public void play() {
        mp.start();
        imgBtn.setTag("play"); // next state should be 'pause'
        imgBtn.setBackgroundResource(R.drawable.pause_selector);
        //start seekbar
        updateSeekbar();
    }

}
خروجی برنامه Music player

خروجی برنامه Music player

نکته ۱) دقت کنید هرجا می بینید خط قرمزی زیر دستوری کشیده شده است، ممکن است به این خاطر باشد که کلاس های مربوط به آن import نشده باشند. با خیال راحت یکبار ctrl + shift + o را فشار دهید تا کلاس هایی که پروژه تان نیاز دارد همی باهم import شوند. نکته ۲) اگر فایل R را همچنان برنامه تان نمیشناسد، چند دلیل دارد که در پست های بعدی آن را بررسی کنیم. اما مهمترین دلیل آن یک اشکال در پوشه res تان است. یا نام فایل های drawable تان از حروف بزرگ،غیرمجاز و یا فاصله تشکیل شده است. ویا در فایل activity_main.xml جایی اشتباه کرده اید و تگ ها را درست ننوشتید.

Facebooktwittergoogle_plusredditpinterestlinkedinmailFacebooktwittergoogle_plusredditpinterestlinkedinmail




16 فکر می‌کنند “آموزش اندروید، فصل ۱۹: Activity (بخش ۲) – اپلیکیشن پخش موسیقی

  1. ali

    حاجی واقعا مرسی
    من که هیچ چیزی از جاوا و اندروید و برنامه نویسی نمیدونستم خیلی خیلی راحت مطالبی رو گفتید رو خوندمو یاد گرفتم.
    خوندن آموزش جاوا منو خیلی مشتاق کرد که اندروید رو هم یاد بگیرم.
    به قول مادربزرگم ” ایشالا دست به خاک میزنی طلا بشه واست”
    واقعا مرسی

    پاسخ
  2. مرتضی

    سلام تنها فایده ای که برام داشت یک نکته پیش پاافتاده اما مهم بود اونم اینکه توی دوتا تابع مختلف میتونیم هی همدیگه رو صدا کنیم
    وگرنه هر کاری کردم سیکبار آپدیت نشد که نشد
    نمیدونم شاید اشکال از منه
    به هر حال ممنون

    پاسخ
  3. محمد

    فکر میکنم خیلی چهشی مطالب رو سنگین کردین من و تو ظراحی قالبش که بعضی جاهارو متوجه نشدم. کد ها هم که یهو خیلی پیشرفته شد. تازه من جاوارو خوندم. اما احساس میکنم مطالب خیلی غیر عادی سنگین شد.
    بازم ممنون

    پاسخ
  4. middle

    سلام خسته نباشید …

    ببخشید میخوام بدونم چجوری در یک اکتیویتی با زدن یک دکمه به عقب برگردیم یا به اکتیویتی قبلی

    پاسخ
  5. علی

    اگر بخواهیم به جای اینکه از raw بگیره موزیک رو از اینترنت بگیره چیکار باید بکنیم؟؟؟ ضروریه، لطفا جواب بدید.ممنون

    پاسخ
  6. bahareh

    سلام
    تشکر بابت آموزش های خوبتون.
    یه سوال داشتم. من میخوام از OnCreate استفاده کنم. از چه کلیدهایی باید استفاده کنم که وقتی O رو تایپ میکنم، لیست همه دستورات بیاد و لازم نباشه که کل متد رو تایپ کنم؟
    خیلی ممنون

    پاسخ

پاسخ دهید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *