实现Android4.4系统设置分页滑动浏览功能

需求描述:

    由于手机功能越来越完善,相应的偏好设置也就越来越多;从用户体验的角度考虑,为了让用户能够在短时间内对常用的偏好设置进行操作,如WIFI,蜂窝数据等。单独将一些常用的设置功能单独展示出来,已达到减少用户操作的可能性。因此采用将系统设置页面进行分页展示并可以滑动进行切换。

效果图:

              



    对于了解Settings源码的同学应该知道,Settings的设计相对来说并不是很简单,大刀阔斧的对Settings可能会引起更多的bug。因此若实现该需求则在保持原有Settings功能完整的情况下进行低耦合高内聚的修改。

    由于Settings是一个Activity,因此,我这里采用ViewPager结合Activity的方法实现该功能。也就是说把Settings作为一个子View嵌入到ViewPager中,同时克隆一个新的Settings为OffenUsedSettings作为另一个字View,该OffenUsedSettings采用与Settings相同的设计思路进行,或者可以完全的复制,只是在载入布局的时候加以改变。具体架构设计如下图:


这里贴出SettingsActivity.java全部代码和OffenUsedActivity.java关键部分代码,其他部分与原有Settings相同。

SettingsActivity.java代码:


package com.android.settings;


import java.util.ArrayList;


import android.app.Activity;
import android.app.LocalActivityManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.view.View;
import android.view.Window;
import android.widget.CompoundButton;
import android.widget.RadioGroup;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.RadioButton;


public class SettingsActivity extends Activity implements OnCheckedChangeListener, OnPageChangeListener {
    private RadioGroup mRadioGroup;
    private RadioButton mOffenUsed, mAll;
    private ViewPager mViewPager;
    private Context mContext;
    private LocalActivityManager mManager;
    private SettingsAdapter mAdapter;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_settings);
        mContext = SettingsActivity.this;
        mManager = new LocalActivityManager(this, false);
        mManager.dispatchCreate(savedInstanceState);
        initView();
    }


    @Override
    protected void onResume() {
        super.onResume();
        mManager.dispatchResume();
        if (mViewPager != null) {
            switch (mViewPager.getCurrentItem()) {
            case 0:
                Activity offenUsedActivity = mManager.getActivity("OFFENUSED");
                if (offenUsedActivity != null && offenUsedActivity instanceof OffenUsedSettings) {
                    ((OffenUsedSettings) offenUsedActivity).invisibleOnScreen();
                }
                break;
            case 1:
                Activity allActivity = mManager.getActivity("ALL");
                if (allActivity != null && allActivity instanceof Settings) {
                    ((Settings) allActivity).invisibleOnScreen();
                }
                break;
            }
        }

    }

    private void initView() {
        mRadioGroup = (RadioGroup) findViewById(R.id.rg_settings);
        mOffenUsed = (RadioButton) findViewById(R.id.rb_offen_used);
        mOffenUsed.setOnCheckedChangeListener(this);
        mAll = (RadioButton) findViewById(R.id.rb_all);
        mAll.setOnCheckedChangeListener(this);
        mViewPager = (ViewPager) findViewById(R.id.vp_settings);
        mViewPager.setOnPageChangeListener(this);


        final ArrayList<View> list = new ArrayList<View>();
        Intent intentCommon = new Intent(mContext, OffenUsedSettings.class);
        list.add(getView("OFFENUSED", intentCommon));
        Intent intentMain = new Intent(mContext, Settings.class);
        list.add(getView("ALL", intentMain));


        mAdapter = new SettingsAdapter(mContext, list);
        mViewPager.setAdapter(mAdapter);


        if (mOffenUsed.isChecked()) {
            mViewPager.setCurrentItem(0);
            mRadioGroup.setBackgroundResource(R.drawable.settings_common);
        } else if (mAll.isChecked()) {
            mViewPager.setCurrentItem(1);
            mRadioGroup.setBackgroundResource(R.drawable.settings_main);
        }
    }


    private View getView(String id, Intent intent) {
        return mManager.startActivity(id, intent).getDecorView();
    }


    @Override
    public void onCheckedChanged(CompoundButton arg0, boolean arg1) {
        if (arg1) {
            switch (arg0.getId()) {
            case R.id.rb_offen_used:
                mViewPager.setCurrentItem(0);
                Activity offenUsedActivity = mManager.getActivity("OFFENUSED");
                if (offenUsedActivity != null && offenUsedActivity instanceof OffenUsedSettings) {
                    ((OffenUsedSettings) offenUsedActivity).invisibleOnScreen();
                }
                break;


            case R.id.rb_all:
                mViewPager.setCurrentItem(1);
                Activity allActivity = mManager.getActivity("ALL");
                if (allActivity != null && allActivity instanceof Settings) {
                    ((Settings) allActivity).invisibleOnScreen();
                }
                break;
            }
        }
    }


    @Override
    public void onPageScrollStateChanged(int arg0) {
    }


    @Override
    public void onPageScrolled(int arg0, float arg1, int arg2) {
    }


    @Override
    public void onPageSelected(int arg0) {
        switch (arg0) {
        case 0:
            if (!mOffenUsed.isChecked())
                mOffenUsed.setChecked(true);
            mRadioGroup.setBackgroundResource(R.drawable.settings_common);
            break;
        case 1:
            if (!mAll.isChecked())
                mAll.setChecked(true);
            mRadioGroup.setBackgroundResource(R.drawable.settings_main);
            break;
        }
    }
}


这里主要对上面代码中蓝色红色部分进行简单描述,其他都是很基础的部分我相信这里不需要我在赘述。

红色代码部分主要将当前Activity作为一个容器以便于可以将其他的Activity作为子View嵌入其中。需要注意的是第二个参数,源码中给出的解释是@param singleMode Ture if the LocalActivityManager should keep a maximum of one activity resume.我这里理解的大概的意思再创建子Activity的时候是否执行所有子Activity的onResume的方法,如果为true,则只执行ViewPager中最后一个创建的Activity的onResume()方法。(这种结论也得到了证实,如果这里为true,则ViewPager中只有最后一个子Activity中的onResume()方法被调用)因此这里我们给出的实参是false。

蓝色部分代码的原因是,ViewPager+Activity的方式会导致子Activity中生命周期函数的紊乱,当然在第一次创建子Activity的时候onResume()方法会正常执行,但是当我们创建成功后,按home键将应用程序切换到后台的时候,在切换回活动状态的时候子Activity的onResume()方法并不会正确调用,但是最外层中的Activity,也就是这里的SettingsActivity方法中的onRuesme()方法会正常被调用,因此这部分代码可以解决这个问题。

OffenUsedSettings.java


    /**
     * Populate the activity with the top-level headers.
     */
    @Override
    public void onBuildHeaders(List<Header> headers) {
        if (!onIsHidingHeaders()) {
            PDebug.Start("loadHeadersFromResource");
 
loadHeadersFromResource(R.xml.offen_used_settings_headers, headers);

            PDebug.End("loadHeadersFromResource");
            updateHeaderList(headers);
        }
    }


以上紫色代码中主要是在载入不同的布局文件。其他与原有的Settings无异。

因此代码解释到这里已经节本完成了,那么值得再叨叨的是,既然OffenusedSettings.java中只是修改以实现载入不同的布局文件来达到前文叙述的需求,为什么不同时使用两个Settings作为子Activity呢?

答案是可定的。我们完全可以把两个Settings同时作为子Activity,只是载入的布局文件不同来达到需求。我已经验证过,这里为了让大家容易理解,便采用了复制Settings的方式来实现。有兴趣的朋友可以自己动手实现。



郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。