Android学习之 换肤功能模块的实现<二>

    在上篇中 主要有学习到皮肤资源内置到应用程序中 的方式实现换肤的 基本思路,本篇将继续以上篇的思路学习 皮肤资源内置的方式实现换肤效果、但本篇侧重于应用中换肤功能的代码设计实现上!切换的皮肤资源位于assets下不同的皮肤资源文件夹中。

    本篇demo程序的代码结构如下:

     

本篇实现换肤功能的代码设计 UML类图如下:


本篇demo的换肤效果如下:

      


      


主要的实现代码在于:

1、SkinConfigManager.java  

作用:皮肤配置管理,封装了SharedPreferences对选择皮肤的序号储存、以及根据皮肤类型锁定assets下的皮肤资源。 详细代码如下:

package com.ice.skininnerdemo;

import android.content.Context;
import android.content.SharedPreferences;

/**
 * 皮肤配置管理<单例> </br>
 * 封装了 SharedPreferences 对象的储存操作
 * Created by ice on 14-10-9.
 */
public class SkinConfigManager {

    private static SkinConfigManager mSkinConfigManager;
    public static final String SKINCONFIG = "SkinConfig";
    public static final String CURSKINTYPEKEY = "curSkinTypeKey";

    private static SharedPreferences mSharedPreferences;

    private SkinConfigManager(Context context){
        mSharedPreferences = context.getSharedPreferences(SKINCONFIG, 0);
    }

    public synchronized static SkinConfigManager getInstance(Context context) {
        if (mSkinConfigManager == null) {
            mSkinConfigManager = new SkinConfigManager(context);
        }
        return mSkinConfigManager;
    }


    /**
     * 设置储存当前选择的皮肤类型值(int 类型值)到 SharedPreferences
     * @param skinType  皮肤类型
     */
    public void setCurSkinType(int skinType){
        SharedPreferences.Editor editor = mSharedPreferences.edit();
        editor.putInt(CURSKINTYPEKEY, skinType);
        editor.commit();
    }


    /**
     * 获得当前储存在SharedPreferences中的 当前皮肤类型<CURSKINTYPE> 值
     * @return
     */
    public int getCurSkinType(){
        if (mSharedPreferences != null) {
            return mSharedPreferences.getInt(CURSKINTYPEKEY, 0);
        }
        return 0;
    }


    /**
     * 获得assets文件夹下面当前皮肤资源所对应的皮肤文件夹名
     * @return
     */
    public String getSkinFileName () {
        String skinFileName = null;
        switch (getCurSkinType()){
            case 0:
                // 默认的皮肤类型
                break;
            case 1:
                skinFileName = "skin_blue";
                break;
            case 2:
                skinFileName = "skin_orange";
                break;
            case 3:
                skinFileName = "skin_red";
                break;
        }
        return skinFileName;
    }


    public SharedPreferences getSkinConfigPreferences () {
        return mSharedPreferences;
    }
}

2、SkinManager.java   

作用:皮肤资源管理器,用于获取assets文件下不同皮肤类型的图片资源文件

详细代码如下:

package com.ice.skininnerdemo;

import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;

import java.io.IOException;
import java.io.InputStream;

/**
 * Created by ice on 14-10-8.
 * 皮肤资源管理器<单例>
 */
public class SkinManager {

    private static SkinManager mSkinManager;
    private AssetManager mAssetManager;

    private SkinManager (Context context) {
        this.mAssetManager = context.getAssets();
    }

    public synchronized static SkinManager getInstance(Context context){

        if (mSkinManager == null) {
            mSkinManager = new SkinManager(context);
        }
        return mSkinManager;
    }


    /**
     * 根据皮肤文件名 和 资源文件名 获取Assets 里面的皮肤资源Drawable对象
     * @param skinFileName  皮肤文件名
     * @param fileName   资源文件名
     * @return
     */
    public Drawable getSkinDrawable(String skinFileName, String fileName) {
        Drawable drawable = null;
        try {
            InputStream inputStream = mAssetManager.open(skinFileName + "/" + fileName);
            drawable = Drawable.createFromStream(inputStream, null);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return drawable;
    }


    /**
     * 根据皮肤文件名 和 资源文件名 获取Assets 里面的皮肤资源Bitmap对象
     * @param skinFileName
     * @param fileName
     * @return
     */
    public Bitmap getSkinBitmap(String skinFileName, String fileName){
        Bitmap image = null;
        try {
            InputStream inputStream = mAssetManager.open(skinFileName + "/" + fileName);
            image = BitmapFactory.decodeStream(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return image;
    }

}

3、SkinableActivity.java 

作用:换肤Activity抽象类,如果说应用中某个Activiy有换肤的需求,那么就继承SkinableActivity吧!

它主要实现了OnSharedPreferenceChangeListener 监听接口,并且注册了SharedPreferences内容改变的事件监听。

在监听到SharedPreferences发生改变的同时回调changeSkin()抽象方法,该方法由SkinableActivity的子类具体实现UI上皮肤资源的更换。

详细代码如下:

package com.ice.skininnerdemo;

import android.app.Activity;
import android.content.SharedPreferences;

/**
 * 换肤Activity抽象类
 * Created by ice on 14-10-8.
 */
public abstract class SkinableActivity extends Activity implements SharedPreferences.OnSharedPreferenceChangeListener{

    @Override
    protected void onStart() {
        super.onStart();
        initSkin();
    }


    /**
     *  初始化皮肤
     */
    private void initSkin() {
        changeSkin();
        // 注册监听,监听换肤的通知
        SkinConfigManager.getInstance(this).getSkinConfigPreferences()
                .registerOnSharedPreferenceChangeListener(this);

    }


    /**
     * sharedPreferences 内容发生改变时触发
     * @param sharedPreferences
     * @param key sharedPreferences中的key值
     */
    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        if (SkinConfigManager.CURSKINTYPEKEY.equals(key)) {
            changeSkin();
        }
    }


    @Override
    protected void onStop() {
        super.onStop();
        SkinConfigManager.getInstance(this).getSkinConfigPreferences()
                .unregisterOnSharedPreferenceChangeListener(this);
    }


    /**
     * 更改设置皮肤,SkinableActivity子类必须要实现该方法 完成换肤过程
     */
    protected abstract void changeSkin();

}

4、SkinSettingActivity.java

作用:皮肤设置Activity,继承了SkinableActivity,实现了changeSkin()抽象方法,完成了皮肤设置UI上的皮肤更换。

(应用中其他需要换肤的Activity类似、主要在changeSkin()方法中实现界面的换肤需求)

详细代码如下:

package com.ice.skininnerdemo;

import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * 皮肤设置Activity
 * Created by ice on 14-10-10.
 */
public class SkinSettingActivity extends SkinableActivity{

    private static final String TAG = "SkinSettingActivity";
    private GridView gv_skin_type;
    private TextView tv_skin_cur;
    private TextView tv_title_skin_setting;

    private int[] skinTypeImage = new int[]{
            R.drawable.overview_skin_default, R.drawable.overview_skin_blue,
            R.drawable.overview_skin_orange, R.drawable.overview_skin_red
    };

    private int[] skinTypeName = new int[]{
            R.string.skin_default, R.string.skin_blue,
            R.string.skin_orange, R.string.skin_red
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_skin_setting);
        initView();
        bindEvent();
    }


    private void initView() {
        tv_title_skin_setting = (TextView)findViewById(R.id.tv_title_skin_setting);
        tv_skin_cur = (TextView)findViewById(R.id.tv_skin_cur);
        gv_skin_type = (GridView)findViewById(R.id.gv_skin_type);

        setGridViewAdapter(gv_skin_type);
    }


    private void setGridViewAdapter(GridView mGridView) {
        List<HashMap<String, Object>> data = new ArrayList<HashMap<String, Object>>();
        int length = skinTypeImage.length;
        for(int i=0; i<length; i++){
            HashMap<String,Object> map = new HashMap<String, Object>();
            map.put("skinTypeImage", skinTypeImage[i]);
            map.put("skinTypeName", getString(skinTypeName[i]));
            data.add(map);
        }

        SimpleAdapter simpleAdapter = new SimpleAdapter(
                this,
                data,
                R.layout.traffic_item,
                new String[]{"skinTypeImage", "skinTypeName"},
                new int[]{R.id.iv_traffic, R.id.tv_trafficName});

        mGridView.setAdapter(simpleAdapter);
    }


    private void bindEvent() {
        gv_skin_type.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                tv_skin_cur.setText(skinTypeName[i]);
                // 将当前选择的皮肤对应的序号 储存到 SharedPreferences 中
                SkinConfigManager.getInstance(SkinSettingActivity.this).setCurSkinType(i);
            }
        });
    }


    @Override
    protected void changeSkin() {
        SkinConfigManager mSkinConfigManager = SkinConfigManager.getInstance(SkinSettingActivity.this);
        String skinFileName = mSkinConfigManager.getSkinFileName();
        Log.d(TAG, "changeSkin() 被执行 / skinFileName: " + skinFileName);

        // 获取当前正在使用的皮肤序号
        int skinType = mSkinConfigManager.getCurSkinType();
        tv_skin_cur.setText(skinTypeName[skinType]);

        if(skinFileName != null){
            Drawable drawable = SkinManager.getInstance(this).getSkinDrawable(skinFileName, "bg_title.9.png");
            tv_title_skin_setting.setBackground(drawable);
        } else {
            tv_title_skin_setting.setBackgroundResource(R.drawable.bg_title);
        }
    }

}


-- -- ok、本篇demo实现换肤效果的主要代码、设计思路于此!小吕其实还想过另外一种皮肤资源的配置方式,

大体想法如下:

1、在assets文件下下面新建一个皮肤资源的配置文件 如:skin.properties 或是 skin_configure.xml

2、所有的皮肤资源(图片)将会放在res/drawable/下面,而什么皮肤类型所对应的皮肤资源图片将会在1中的skin.properties 或是 skin_configure.xml中 通过配置的方式引用。


当然  这只是小吕目前的一个初步想法及设计思想、如果 看到这里的您 如果还有其他实现换肤的设计思想、还希望大气的给小吕留言、学习、和交流。


补充内容:本demo中 有关于获取assets文件中的.9格式图片。

                   关于获取assets下面的.9格式图片问题,这里是很有趣的,

                   该demo中assets下的.9格式图片 都是先使用 AAPT 命令编译处理后的,及被Android系统编译过的。

                   那为什么要这样做呢?大家可以先自己研究下 或是 在网上搜索答案、小吕后面有时间将会整理成博客。


最后附上本篇demo的代码<免下载积分>: http://download.csdn.net/detail/l416112167/8027581

-----------------

下篇  小吕将会学习与介绍第二种实现方式:  [皮肤资源与应用程序分离]




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