简单易用的仿 PreferenceActivity 的自定义Preference管理(支持服务器同步)

本文介绍了一种自定义偏好设置组件的设计与实现方法,该组件能够简化SharePreference的管理和使用,支持与服务器同步、自定义视图显示等功能。

开发动机

移动端的配置离不开SharePreference,如果我们直接操作SharePreference就需要重复写界面来管理,每次都要做重复的工作,很是麻烦。
想到了谷歌的原生PreferenceActivity,去看了PreferenceScreen相关的源码想把PreferenceScreen抽离出来作为单独的一个组件,可以随意的加入到任何界面中。几经努力后失败了,失败原因如下:

  • PreferenceManager 只能被反射调用
  • GenericInflater.Parent 包外不能访问
  • 不支持网络同步

基于如上原因,所以决定自己写一个Preference的组件来解除重复劳动

开发思路

我们需要的Preference组件需要支持如下功能:

  • 保存读取SharePreference
  • 可以和服务器进行同步
  • 每项Preference支持自定义的view 显示
  • 简单易用
  • 高度的灵活和定制

根据以上功能,最终决定整个组件是一个ListView,每一个item 包含了SharePreference的key,提供自定义view,点击响应,文字显示,和网络同步接口。给ListView的adapter设置items源,则完成绑定。

代码组成

  • SettingItem(item基类)
  • SettingItemText(继承自SettingItem,只有文字显示的item,点击跳转)
  • SettingItemSwitch(继承自SettingItem,包含switch开关的item,点击switch切换开关)
  • SettingItemScreen(绑定listview和items源,便于管理)
  • SettingKeyValue(管理所有KEY,可以静态访问Value)
SettingItem
public abstract class SettingItem {
    protected String mPrefKey;
    protected String mTitle;
    protected String mSummary;

    public String getKey() {
        return mPrefKey;
    }

    public void setKey(String key) {
        mPrefKey = key;
    }

    public String getTitle() {
        return mTitle;
    }

    public void setTitle(String title) {
        mTitle = title;
    }

    public String getSummary() {
        return mSummary;
    }

    public void setSummary(String text) {
        mSummary = text;
    }

    public abstract View createView();

    public boolean getState() {
        return getBooleanValue();
    }

    public void setState(boolean isOn) {
        setBooleanValue(isOn);
    }

    public abstract void bindView(View view);

    //将服务器拉下来
    public abstract void refreshData();

    //将SP上传服务器
    public abstract void syncNetwork();

    //SP是否有值,如果没有值,需要从服务器拉取
    public boolean isSPUnknow() {
        return SettingKeyValue.isSPUnknow(mPrefKey);
    }

    public boolean getBooleanValue() {
        return SettingKeyValue.getBooleanValue(mPrefKey);
    }

    public void setBooleanValue(boolean value) {
        SettingKeyValue.setBooleanValue(mPrefKey, value);
    }
}
SettingItemText
public class SettingItemText extends SettingItem {

    private View.OnClickListener mOnClickListener;

    public SettingItemText(String title, String summary, View.OnClickListener onClickListener) {
        this.mTitle = title;
        this.mSummary = summary;
        mOnClickListener = onClickListener;
    }

    //用于ListView 创建View
    @Override
    public View createView() {
        View view = LayoutInflater.from(HomeApplication.getInstance()).inflate(
                R.layout.item_text_setting,
                null,
                false);
        ViewHolder viewHolder = new ViewHolder();
        viewHolder.title = (TextView) view.findViewById(R.id.title);
        viewHolder.summary = (TextView) view.findViewById(R.id.summary);
        view.setTag(viewHolder);
        return view;
    }

    @Override
    public boolean getState() {
        return false;
    }

    @Override
    public void setState(boolean isOn) {

    }

    @Override
    public void bindView(View view) {
        view.setClickable(true);
        ViewHolder viewHolder = (ViewHolder) view.getTag();
        viewHolder.title.setText(mTitle);
        viewHolder.summary.setText(mSummary);
        if (mOnClickListener != null) {
            view.setOnClickListener(mOnClickListener);
        }
    }

    @Override
    public void refreshData() {

    }

    @Override
    public void syncNetwork() {

    }

    public static class ViewHolder {
        TextView title;
        TextView summary;
    }

}
SettingItemSwitch

这里的SwitchView是一个自定义通用的view,所以需要稍微适配一下

public abstract class SettingItemSwitch extends SettingItem {

    private SwitchView.OnSwitchStatusChangeListener mOnSwitchClickListener;

    public SettingItemSwitch(String title, String summary, String key) {
        mTitle = title;
        mSummary = summary;
        mPrefKey = key;
        mOnSwitchClickListener = new SwitchView.OnSwitchStatusChangeListener() {
            @Override
            public void onSwitchStatusChange(SwitchView switchView, int switchStatus) {
                setState(switchStatus == SwitchView.SWITCH_ON);
            }
        };
    }

    @Override
    public View createView() {
        View view = LayoutInflater.from(HomeApplication.getInstance()).inflate(
                R.layout.item_switch_setting,
                null,
                false);
        ViewHolder viewHolder = new ViewHolder();
        viewHolder.title = (TextView) view.findViewById(R.id.title);
        viewHolder.summary = (TextView) view.findViewById(R.id.summary);
        viewHolder.switchView = (SwitchView) view.findViewById(R.id.switch_view);
        view.setTag(viewHolder);
        return view;
    }

    @Override
    public void bindView(View view) {
        ViewHolder viewHolder = (ViewHolder) view.getTag();
        viewHolder.title.setText(mTitle);
        if (TextUtils.isEmpty(mSummary)) {
            viewHolder.summary.setVisibility(View.GONE);
        } else {
            viewHolder.summary.setText(mSummary);
        }
        viewHolder.switchView.initSwitchStatus(getState());
        viewHolder.switchView.setOnSwitchStatusChangeListener(mOnSwitchClickListener);
    }

    @Override
    public abstract void refreshData();

    public abstract void syncNetwork();

    public static class ViewHolder {
        TextView title;
        TextView summary;
        SwitchView switchView;
    }
}
SettingItemScreen
public class SettingItemScreen{
    private ListView mListView;
    private List<SettingItem> mItemList;
    private BaseAdapter mListAdapter;
    private Map<String,Integer> mTypeMap;
    public SettingItemScreen(@NotNull ListView listView, @NotNull List<SettingItem> itemList){
        mItemList = itemList;
        createType(mItemList);
        mListView = listView;
        mListAdapter = new BaseAdapter() {
            @Override
            public int getCount() {
                return mItemList.size();
            }


            @Override
            public int getItemViewType(int position) {
                String type = getItem(position).getClass().toString();
                return mTypeMap.get(type);
            }

            @Override
            public int getViewTypeCount() {
                return mTypeMap.size();
            }

            @Override
            public Object getItem(int position) {
                return mItemList.get(position);
            }

            @Override
            public long getItemId(int position) {
                return position;
            }

            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                View view = convertView;
                SettingItem settingItem = mItemList.get(position);
                if(view == null){
                    view = settingItem.createView();
                }
                settingItem.bindView(view);
                return view;
            }
        };
        mListView.setAdapter(mListAdapter);
    }

    private void createType(List<SettingItem> mItemList){
        int i = 0;
        mTypeMap = new HashMap<>();
        for(SettingItem settingItem : mItemList){
            String type = settingItem.getClass().toString();
            if(mTypeMap.get(type) == null){
                mTypeMap.put(type,i++);
            }
        }
    }

    public void refresh(){
        int size = mItemList.size();
        for(int i = 0 ; i < size ; i++ ){
            SettingItem settingItem = mItemList.get(i);
            settingItem.refreshData();
        }
    }

    public void sync(){
        int size = mItemList.size();
        for(int i = 0 ; i < size ; i++ ){
            SettingItem settingItem = mItemList.get(i);
            settingItem.syncNetwork();
        }
    }

    public void refreshView(){
        mListAdapter.notifyDataSetChanged();
    }

}

Demo

代码请移步Demo
效果图
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值