3.2.3 Activity的使用技巧汇总

作者: 常思行 | 来源:发表于2018-06-01 09:56 被阅读17次

    一、横竖屏切换与状态保存的问题

    前面的文章说到了App横竖屏切换的时候会销毁当前的Activity然后重新创建一个,我们可以自行在生命周期的每个方法里都添加打印Log的语句,以此来进行判断。又或者设一个按钮一个TextView点击按钮后,修改TextView 文本,然后横竖屏切换,这时我们就会发现TextView文本变回之前的内容了。

    总之,横竖屏切换时Activity走下述生命周期:onPause-> onStop-> onDestory-> onCreate->onStart->onResume。也就是说Activity销毁后又重新建立了一个,下面我们说一下关于横竖屏切换可能遇到的问题以及解决方法。

    1、禁止屏幕横竖屏自动切换

    解决方法很简单,我们在AndroidManifest.xml中为Act添加一个android:screenOrientation属性就可以了。

    android:screenOrientation有下述可选值:
    • unspecified:默认值,由系统来判断显示方向,判定的策略是和设备相关的,所以不同的设备会有不同的显示方向。
    • landscape:横屏显示(宽比高要长)。
    • portrait:竖屏显示(高比宽要长)。
    • user:用户当前首选的方向。
    • behind:和该Activity下面的那个Activity的方向一致(在Activity堆栈中的)。
    • sensor:由物理的感应器来决定,如果用户旋转设备这屏幕会横竖屏切换。
    • nosensor:忽略物理感应器,这样就不会随着用户旋转设备而更改了("unspecified"设置除外)。

    2、横竖屏时想加载不同的布局

    (1)方法一:准备两套不同的布局

    Android会自己根据横竖屏加载不同布局,方法是创建两个布局文件夹:layout-land横屏、layout-port竖屏,然后把这两套布局文件丢这两文件夹里,文件名一样,Android就会自行判断,然后加载相应布局了!

    (1)方法二:自己在代码中进行判断

    这样的话我们就能自己想加载什么就加载什么,我们一般是在onCreate()方法中加载布局文件的,我们可以在这里对横竖屏的状态做下判断,关键代码如下:

    if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE){  
         setContentView(R.layout.横屏);
    }  
    
    else if (this.getResources().getConfiguration().orientation ==Configuration.ORIENTATION_PORTRAIT) {  
        setContentView(R.layout.竖屏);
    }
    

    3、状态保存问题

    这个其实我们之前说过了,通过一个Bundle savedInstanceState参数即可完成,三个核心方法是:

    onCreate(Bundle savedInstanceState);
    onSaveInstanceState(Bundle outState);
    onRestoreInstanceState(Bundle savedInstanceState);
    

    然后重写onSaveInstanceState()方法,往这个bundle中写入数据,比如:

    outState.putInt("num",1);
    

    然后你在onCreate或者onRestoreInstanceState中就可以拿出里面存储的数据,不过拿之前要判断下是否为null:

    savedInstanceState.getInt("num");
    

    二、知晓当前是哪个Activity

    这个技巧将教会你,如何根据程序当前的界面就能判断出这是哪一个Activity。可能你会觉得挺纳闷的,我自己写的代码怎么会不知道这是哪一个Activity呢?很不幸的是,在你真正进入到企业之后,更有可能的是接手一份别人写的代码,因为你刚进公司就正好有一个新项目启动的概率并不高。阅读别人的代码时有一个很头疼的问题,就是你需要在某个界面上修改一些非常简单的东西,但是你半天找不到这个界面对应的活动是哪一个。学会了本节的技巧之后,这对你来说就再也不是难题了。

    首先需要新建一个 BaseActivity 继承自Activity,然后在 BaseActivity 中重写 onCreate()方法,如下所示:

    public class BaseActivity extends Activity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Log.d(TAG, getClass().getSimpleName());
        }
    }
    

    这样我们就在 onCreate() 方法中获取了当前实例的类名,并通过 Log 打印了出来。

    接下来我们需要让 BaseActivity 成为工程项目中所有具有共同属性的Activity的父类,这样虽然项目中的Activity不再直接继承自 Activity 了,但是它们仍然完全继承了 Activity 中的所有特性,并且加入了我们最新的功能。

    三、随时随地完全退出App以及退出指定Activity的方法

    1、完全退出App

    (1)完全退出Activity

    有时我们可能会打开了很多个Activity,突然来个这样的需求,在某个页面可以关掉所有的Activity并退出程序。别以为这不会是真的,比如如果目前你手机的界面还停留在 SecondActivity,你会发现当前想退出程序是非常不方便的,需要连按两次 Back 键才行。有可能你会说:按Home键就行了呀。但是按 Home 键只是把程序挂起,并没有退出程序。所以这个问题就足以引起我们的思考,如果我们的程序需要一个注销或者退出的功能该怎么办呢?必须要有一个随时随地都能退出程序的方案才行。

    好吧,下面提供一个关闭所有Activity的方法, 就是用一个list集合来管理所有Activity,然后就方便后续的退出操作了。

    新建一个 ActivityCollector 类作为活动管理器,代码如下所示:
    public class ActivityCollector {
        public static List<Activity> activities = new ArrayList<Activity>();
        public static void addActivity(Activity activity) {
            activities.add(activity);
        }
        public static void removeActivity(Activity activity) {
            activities.remove(activity);
        }
        public static void finishAll() {
            for (Activity activity : activities) {
                if (!activity.isFinishing()) {
                    activity.finish();
                }
            }
        }
    }
    

    在Activity管理器中,我们通过一个 List 来暂存Activity,然后提供了一个 addActivity() 方法用
    于向 List 中添加一个Activity,提供了一个 removeActivity() 方法用于从 List 中移除Activity,最后提供了一个 finishAll() 方法用于将 List 中存储的Activity全部都销毁掉,这样我们就可以轻松实现我们想要的功能了,比如我们可以这么使用:

        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Log.d("BaseActivity", getClass().getSimpleName());
            ActivityCollector.addActivity(this);
        }
        @Override
        protected void onDestroy() {
            super.onDestroy();
            ActivityCollector.removeActivity(this);
        }
    

    在 onCreate() 方法中调用了 ActivityCollector 的 addActivity()方法,表明将当前正在创建的Activity添加到活动管理器里。然后在 onDestroy() 方法中调用了 ActivityCollector 的 removeActivity() 方法,表明将一个马上要销毁的Activity从活动管理器里移除。

    从此以后,不管我么想在什么地方退出程序,只需要调用 ActivityCollector.finishAll() 方法就可以了。

    (2)杀死整个App

    上面说的是关闭所有Activity的,但是有些时候我们可能想杀死整个App,连后台任务都杀死 杀得一干二净的话,可以使用搭配着下述代码使用:

        /**
         * 退出应用程序
         */
        public void AppExit(Context context) {
            try {
                ActivityCollector.finishAll();
                ActivityManager activityMgr = (ActivityManager) context
                        .getSystemService(Context.ACTIVITY_SERVICE);
                activityMgr.killBackgroundProcesses(context.getPackageName());
                System.exit(0);
            } catch (Exception ignored) {}
        }
    

    2、退出指定Activity的方法

    首先,我们提供一种使用命令行查看当前所有Activity的命令

    使用下述命令即可,前提是你为SDK配置了环境变量:

    adb shell dumpsys activity
    

    其次,我们可以用这个Activity管理类来更精确的管理Activity:

    public class AppManager {
        private static Stack<Activity> activityStack;
        private static AppManager instance;
        private AppManager(){}
        /**
         * 单一实例
         */
        public static AppManager getAppManager(){
            if(instance==null){
                instance=new AppManager();
            }
            return instance;
        }
        /**
         * 添加Activity到堆栈
         */
        public void addActivity(Activity activity){
            if(activityStack==null){
                activityStack=new Stack<Activity>();
            }
            activityStack.add(activity);
        }
        /**
         * 获取当前Activity(堆栈中最后一个压入的)
         */
        public Activity currentActivity(){
            Activity activity=activityStack.lastElement();
            return activity;
        }
        /**
         * 结束当前Activity(堆栈中最后一个压入的)
         */
        public void finishActivity(){
            Activity activity=activityStack.lastElement();
            finishActivity(activity);
        }
        /**
         * 结束指定的Activity
         */
        public void finishActivity(Activity activity){
            if(activity!=null){
                activityStack.remove(activity);
                activity.finish();
                activity=null;
            }
        }
        /**
         * 结束指定类名的Activity
         */
        public void finishActivity(Class<?> cls){
            for (Activity activity : activityStack) {
                if(activity.getClass().equals(cls) ){
                    finishActivity(activity);
                }
            }
        }
        /**
         * 结束所有Activity
         */
        public void finishAllActivity(){
            for (int i = 0, size = activityStack.size(); i < size; i++){
                if (null != activityStack.get(i)){
                    activityStack.get(i).finish();
                }
            }
            activityStack.clear();
        }
        /**
         * 退出应用程序
         */
        public void AppExit(Context context) {
            try {
                finishAllActivity();
                ActivityManager activityMgr=(ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
                activityMgr.restartPackage(context.getPackageName());
                System.exit(0);
            } catch (Exception e) { }
        }
    }
    

    四、双击退出程序的两种方法

    1、定义一个变量来标识是否退出

    private static boolean isExit = false;
    Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            isExit = false;
        }
    };
    
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if (!isExit) {
                isExit = true;
                Toast.makeText(getApplicationContext(), "再按一次退出程序",
                        Toast.LENGTH_SHORT).show();
                // 利用handler延迟发送更改状态信息
                mHandler.sendEmptyMessageDelayed(0, 2000);
            } else {
                exit(this);
            }
            return false;
        }
    return super.onKeyDown(keyCode, event);}
    

    2、保存点击时间

        private long exitTime = 0;
        public boolean onKeyDown(int keyCode, KeyEvent event) {
            if (keyCode == KeyEvent.KEYCODE_BACK) {
                if ((System.currentTimeMillis() - exitTime) > 2000) {
                    Toast.makeText(getApplicationContext(), "再按一次退出程序",
                            Toast.LENGTH_SHORT).show();
                    exitTime = System.currentTimeMillis();
                } else {
                    exit();
                }
                return false;
            }
            return super.onKeyDown(keyCode, event);
        }
    

    五、启动Activity的最佳写法

    启动Activity的方法相信你已经非常熟悉了,首先通过 Intent 构建出当前的“意图”,然后调用 startActivity() 或 startActivityForResult() 方法将Activity启动起来,如果有数据需要从一个Activity传递到另一个Activity,也可以借助 Intent 来完成。

    假设 SecondActivity 中需要用到两个非常重要的字符串参数,在启动 SecondActivity 的时候必须要传递过来,那么我们很容易会写出如下代码:

    Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
    intent.putExtra("param1", "data1");
    intent.putExtra("param2", "data2");
    startActivity(intent);
    

    这样写是完全正确的,不管是从语法上还是规范上,只是在真正的项目开发中经常会有对接的问题出现。比如 SecondActivity 并不是由你开发的,但现在你负责的部分需要有启动 SecondActivity 这个功能,而你却不清楚启动这个Activity需要传递哪些数据。这时无非就有两种办法,一个是你自己去阅读 SecondActivity 中的代码,二是询问负责编写 SecondActivity 的同事,但这样你会不会觉得很麻烦呢?其实只需要换一种写法,就可以轻松解决掉上面的窘境。

    修改 SecondActivity 中的代码,如下所示:
        public class SecondActivity extends BaseActivity {
            public static void actionStart(Context context, String data1, String data2) {
                Intent intent = new Intent(context, SecondActivity.class);
                intent.putExtra("param1", data1);
                intent.putExtra("param2", data2);
                context.startActivity(intent);
            }
        }
    

    我们在 SecondActivity 中添加了一个 actionStart() 方法,在这个方法中完成了 Intent 的构建,另外所有 SecondActivity 中需要的数据都是通过 actionStart() 方法的参数传递过来的,然后把它们存储到 Intent 中,最后调用 startActivity()方法启动 SecondActivity。

    这样写的好处在哪里呢?最重要的一点就是一目了然, SecondActivity 所需要的数据全部都在方法参数中体现出来了,这样即使不用阅读 SecondActivity 中的代码,或者询问负责编写 SecondActivity 的同事,你也可以非常清晰地知道启动 SecondActivity 需要传递哪些数据。另外,这样写还简化了启动活动的代码,现在只需要一行代码就可以启动 SecondActivity,如下所示:

        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                SecondActivity.actionStart(MainActivity.this, "data1", "data2");
            }
        });
    

    养成一个良好的习惯, 给你编写的每个Activity都添加类似的启动方法,这样不仅可以让启动Activity变得非常简单,还可以节省不少你同事过来询问你的时间。

    六、Activity全屏与对话框风格的实现

    (1)Activity全屏风格的实现

    1、代码隐藏ActionBar

    在Activity的onCreate方法中调用getActionBar.hide();,以此隐藏ActionBar。

    2、通过requestWindowFeature设置

    在代码中调用**requestWindowFeature(Window.FEATURE_NO_TITLE); **,不过该代码需要在setContentView ()之前调用,不然会报错。

    不过有的时候这种方法并不能奏效,解决方法有两种:

    (1)将AppCompatActivity改为Activity,此时 requestWindowFeature(Window.FEATURE_NO_TITLE);是有效的。
    (2)在onCreate()方法中加入如下代码:

    if (getSupportActionBar() != null){
       getSupportActionBar().hide();
    }
    
    3、通过AndroidManifest.xml的theme设置

    在需要全屏的Activity的标签内设置 theme = @android:style/Theme.NoTitleBar.FullScreen

    (2)Activity对话框风格的实现

    在某些情况下,我们可能需要将Activity设置成对话框风格的,Activity一般是占满全屏的, 而Dialog则是占据部分屏幕的,实现起来也很简单。

    1、直接设置下Activity的theme
    android:theme="@android:style/Theme.Dialog"
    
    2、设置标题和小图标
    // 设置左上角小图标
    requestWindowFeature(Window.FEATURE_LEFT_ICON);
    setContentView(R.layout.main);
    getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, android.R.drawable.ic_lion_icon);
    // 设置文字
    setTitle(R.string.actdialog_title);  //XML代码中设置:android:label="@string/activity_dialog"
    

    七、为Activity设置过场动画:

    所谓的过场动画就是切换到另外的Activity时加上一些切换动画,比如淡入淡出,放大缩小,左右互推等。当然,我们并不在这里详细讲解动画,后面我们会有专门的篇幅来讲解这个,这里只教大家如何去加载动画,另外给大家提供了一些比较常用的过渡动画,只要将相关动画文件添加到res/anim目录下,然后下述方法二选一 就可以实现Activity的切换动画了。

    方法一:

    Activity A 跳转到 Activity B,在startActivity(intent)后面加上overridePendingTransition(R.anim.anim_in, R.anim.anim_out);
    Activity B 跳转到 Activity A,要在finish()后面加上overridePendingTransition(R.anim.anim_in, R.anim.anim_out);
    其中,anim_in是进入Activity的动画,而anim_out是退出Activity的动画。

    方法二:

    通过style进行配置,这个是全局的,就是所有的Activity都会加载这个动画。

    在style.xml中自定义style:
    <!-- 默认Activity跳转动画 -->
    <style name="default_animation" mce_bogus="1" parent="@android:style/Animation.Activity">
        <item name="android:activityOpenEnterAnimation">@anim/default_anim_in</item>
        <item name="android:activityOpenExitAnimation">@anim/anim_stay</item>
        <item name="android:activityCloseEnterAnimation">@anim/anim_stay</item>
        <item name="android:activityCloseExitAnimation">@anim/default_anim_out</item>
    </style>
    
    其中的四个item分别代表:
    • Activity A跳转到Activity B时Activity B进入动画
    • Activity A跳转到Activity B时Activity A退出动画
    • Activity B返回Activity A时Activity A的进入动画
    • Activity B返回Activity A时ActivityB的退出动画
    然后修改AppTheme:
    <style name="AppTheme" mce_bogus="1" parent="@android:style/Theme.Light">
            <item name="android:windowAnimationStyle">@style/default_animation</item>
            <item name="android:windowNoTitle">true</item>
    </style>
    
    最后在appliction设置下:
    <application
       android:icon="@drawable/logo"
       android:label="@string/app_name"
       android:theme="@style/AppTheme" >
    

    好的,Activity过场动画就这样设置好了。

    八、为Activity设置透明背景

    方法一:

    这种方法比较简单,只有一个步骤,只需要在配置文件中把需要设置为透明的Activity的样式设置为:

    Android:theme="@android:style/Theme.Translucent"
    

    即可,这种方式只改变背景的颜色,对其他控件没有影响,但是它只能把背景设置为完全透明,如果要设置为半透明或者要设置透明的程度无法实现。

    方法二:

    这种方法也比较简单,只需要在方法一的基础上,再布局文件中配置背景颜色就可以:

    android:background="#01000000"  
    

    “#01000000”中的“01”表示的是背景透明的程度,这个值只能设置01及以上的值,不能设置为00,及不能设置为完全透明,不过设置为01其实和透明的效果也很接近了,肉眼几乎看不出来区别了,这种方法同样对其他控件没有影响。

    方法三

    这种方法稍微复杂些,有几个步骤,这种方法对其他控件的透明度也会产生影响,并且可以自己设置透明的程度,相对来说要灵活一些。

    • 第一步
      在res/values下建立colors.xml文件,设置一个背景颜色,在这里可以设置你背景的颜色和透明度。
    <color name="transparent">#55ff</color>  
    
    • 第二步
      在res/values/下建styles.xml,设置程序的风格
    <style name="Transparent">  
            <item name="android:windowBackground">@color/transparent</item>  
            <item name="android:windowIsTranslucent">true</item>  
            <item name="android:windowAnimationStyle">@+android:style/Animation.Translucent</item>  
    </style>  
    
    • 第三步
      把这个styles.xml用在相应的Activity上,即在AndroidManifest.xml中的任意<activity>标签中添加:
    android:theme="@style/Transparent"  
    

    如果想设置所有的activity都使用这个风格,可以把这句标签语句添加在<application>中。

    这个方法不仅对背景透明有效,而且对其他控件也有效,如果其他控件没有设置背景颜色,会呈现出透明的效果。这种方法比较复杂些,如果不是需要对整个页面及控件都有透明度要求,建议使用前面两种方法。

    点此进入:GitHub开源项目“爱阅”

    感谢优秀的你跋山涉水看到了这里,欢迎关注下让我们永远在一起!

    相关文章

      网友评论

        本文标题:3.2.3 Activity的使用技巧汇总

        本文链接:https://www.haomeiwen.com/subject/mdehsftx.html