仿墨迹天气在Android App中实现自定义zip皮肤更换

在这里谈一下墨迹天气的换肤实现方式,不过首先声明我只是通过反编译以及参考了一些网上其他资料的方式推测出的换肤原理, 在这里只供参考. 若大家有更好的方式, 欢迎交流.
墨迹天气下载的皮肤就是一个zip格式的压缩包,在应用的时候把皮肤资源释放到墨迹天气应用的目录下,更换皮肤时新的皮肤资源会替换掉老的皮肤资源每次加载的时候就是从手机硬盘上读取图片,这些图片资源的命名和程序中的资源的命名保持一致,一旦找不到这些资源,可以选择到系统默认中查找。这种实现是直接读取了外部资源文件,在程序运行时通过代码显示的替换界面的背景资源。这种方式的优点是:皮肤资源的格式定义很随意可以是zip也可以是自定义的格式,只要程序中能够解析到资源就行,缺点是效率上的问题.

这里需要注意的一点是,再这里对压缩包的解压,借助了第三方工具: ant. jar进行解压和压缩文件. 关于ant工具的使用,我在稍后的文章中会具体介绍.

主要技术点:
如何去读取zip文件中的资源以及皮肤文件存放方式

实现方案:如果软件每次启动都去读取SD卡上的皮肤文件,速度会比较慢。较好的做法是提供一个皮肤设置的界面,用户选择了哪一个皮肤,就把那个皮肤文件解压缩到”/data/data/[package name]/skin”路径下(读取的快速及安全性),这样不需要跨存储器读取,速度较快,而且不需要每次都去zip压缩包中读取,不依赖SD卡中的文件,即使皮肤压缩包文件被删除了也没有关系。
实现方法:
1. 在软件的帮助或者官网的帮助中提示用户将皮肤文件拷贝到SD卡指定路径下。
2. 在软件中提供皮肤设置界面。可以在菜单或者在设置中。可参考墨迹、搜狗输入法、QQ等支持换肤的软件。
3. 加载指定路径下的皮肤文件,读取其中的缩略图,在皮肤设置界面中显示,将用户选中的皮肤文件解压缩到”/data/data/[package name]/skin”路径下。
4. 软件中优先读取”/data/data/[package name]/skin/”路径下的资源。如果没有则使用apk中的资源。

效果图:

具体代码:
1. AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.tony.skin" android:versionCode="1" android:versionName="1.0">
  <uses-sdk android:minSdkVersion="7" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  <application android:icon="@drawable/icon" android:label="@string/app_name">
    <activity android:name=".Re_Skin2Activity"
         android:label="@string/app_name">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity> 

  </application>
</manifest>

2.布局文件main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:background="#d2d2d2"
  android:id="@+id/layout">
  <Button android:text="导入皮肤" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
  <Button android:text="换肤" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
  <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content"
     android:text="请先点击“导入皮肤”,会将/sdcard/skin.zip导入到/sdcard/Skin_kris目录下,然后点击‘换肤'会将sdcard里面的素材用作皮肤"
     android:textColor="#000"></TextView>
</LinearLayout>

3. Re_Skin2Activity:

package com.tony.skin; 

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast; 

import com.tony.skin.utils.ZipUtil; 

/**
 *
 * @author Tony
 *
 */
public class Re_Skin2Activity extends Activity implements OnClickListener{
  private Button btnSet;
  private Button btnImport;
  private LinearLayout layout;
  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    btnSet = (Button)findViewById(R.id.button1);
    btnSet.setOnClickListener(this); 

    btnImport = (Button)findViewById(R.id.button2);
    btnImport.setOnClickListener(this);
    layout = (LinearLayout)findViewById(R.id.layout);
  }
  @Override
  public void onClick(View v) {
    switch (v.getId()) {
    case R.id.button1:
      Bitmap bitmap= BitmapFactory.decodeFile("/sdcard/tony/skin/skin.png"); 

       BitmapDrawable bd=new BitmapDrawable(bitmap);
      btnSet.setBackgroundDrawable(bd); 

      layout.setBackgroundDrawable(new BitmapDrawable(BitmapFactory.decodeFile("/sdcard/Skin_kris/skin/bg/bg.png"))); 

      break;
    case R.id.button2:
      ZipUtil zipp = new ZipUtil(2049);
      System.out.println("begin do zip");
      zipp.unZip("/sdcard/skin.zip","/sdcard/Skin_kris");
      Toast.makeText(this, "导入成功", Toast.LENGTH_SHORT).show();
      break;
    default:
      break;
    }
  }
}

4. ZipUtil 解压缩处理ZIP包的工具类

package com.tony.skin.utils; 

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.zip.Deflater; 

import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import org.apache.tools.zip.ZipOutputStream; 

/**
 * Zip包压缩,解压处理工具类
 * @author a
 *
 */
public class ZipUtil {
  private ZipFile     zipFile;
  private ZipOutputStream zipOut;   //压缩Zip
  private int      bufSize;  //size of bytes
  private byte[]     buf;
  private int       readedBytes;
  public ZipUtil(){
    this(512);
  }  

  public ZipUtil(int bufSize){
    this.bufSize = bufSize;
    this.buf = new byte[this.bufSize];
  }  

  /**
   *
   * @param srcFile 需要 压缩的目录或者文件
   * @param destFile 压缩文件的路径
   */
  public void doZip(String srcFile, String destFile) {// zipDirectoryPath:需要压缩的文件夹名
    File zipDir;
    String dirName; 

    zipDir = new File(srcFile);
    dirName = zipDir.getName();
    try {
      this.zipOut = new ZipOutputStream(new BufferedOutputStream(
          new FileOutputStream(destFile)));
      //设置压缩的注释
      zipOut.setComment("comment");
      //设置压缩的编码,如果要压缩的路径中有中文,就用下面的编码
      zipOut.setEncoding("GBK");
      //启用压缩
      zipOut.setMethod(ZipOutputStream.DEFLATED);  

      //压缩级别为最强压缩,但时间要花得多一点
      zipOut.setLevel(Deflater.BEST_COMPRESSION);  

      handleDir(zipDir, this.zipOut,dirName);
      this.zipOut.close();
    } catch (IOException ioe) {
      ioe.printStackTrace();
    }
  } 

  /**
   * 由doZip调用,递归完成目录文件读取
   * @param dir
   * @param zipOut
   * @param dirName 这个主要是用来记录压缩文件的一个目录层次结构的
   * @throws IOException
   */
  private void handleDir(File dir, ZipOutputStream zipOut,String dirName) throws IOException {
    System.out.println("遍历目录:"+dir.getName());
    FileInputStream fileIn;
    File[] files; 

    files = dir.listFiles(); 

    if (files.length == 0) {// 如果目录为空,则单独创建之.
      // ZipEntry的isDirectory()方法中,目录以"/"结尾.
      System.out.println("压缩的 Name:"+dirName);
      this.zipOut.putNextEntry(new ZipEntry(dirName));
      this.zipOut.closeEntry();
    } else {// 如果目录不为空,则分别处理目录和文件.
      for (File fileName : files) {
        // System.out.println(fileName); 

        if (fileName.isDirectory()) {
          handleDir(fileName, this.zipOut,dirName+File.separator+fileName.getName()+File.separator);
        } else {
          System.out.println("压缩的 Name:"+dirName + File.separator+fileName.getName());
          fileIn = new FileInputStream(fileName);
          this.zipOut.putNextEntry(new ZipEntry(dirName + File.separator+fileName.getName())); 

          while ((this.readedBytes = fileIn.read(this.buf)) > 0) {
            this.zipOut.write(this.buf, 0, this.readedBytes);
          } 

          this.zipOut.closeEntry();
        }
      }
    }
  } 

  /**
   * 解压指定zip文件
   * @param unZipfile 压缩文件的路径
   * @param destFile   解压到的目录 
   */
  public void unZip(String unZipfile, String destFile) {// unZipfileName需要解压的zip文件名
    FileOutputStream fileOut;
    File file;
    InputStream inputStream; 

    try {
      this.zipFile = new ZipFile(unZipfile); 

      for (Enumeration entries = this.zipFile.getEntries(); entries
          .hasMoreElements();) {
        ZipEntry entry = (ZipEntry) entries.nextElement();
        file = new File(destFile+File.separator+entry.getName()); 

        if (entry.isDirectory()) {
          file.mkdirs();
        } else {
          // 如果指定文件的目录不存在,则创建之.
          File parent = file.getParentFile();
          if (!parent.exists()) {
            parent.mkdirs();
          } 

          inputStream = zipFile.getInputStream(entry); 

          fileOut = new FileOutputStream(file);
          while ((this.readedBytes = inputStream.read(this.buf)) > 0) {
            fileOut.write(this.buf, 0, this.readedBytes);
          }
          fileOut.close(); 

          inputStream.close();
        }
      }
      this.zipFile.close();
    } catch (IOException ioe) {
      ioe.printStackTrace();
    }
  } 

  // 设置缓冲区大小
  public void setBufSize(int bufSize) {
    this.bufSize = bufSize;
  }
}
(0)

相关推荐

  • 在Android系统中使用gzip进行数据传递实例代码

    接下来,让我解说一下如何在Android系统中使用gzip进行数据传递 HTTP协议上的GZIP编码是一种用来改进WEB应用程序性能的技术.大流量的WEB站点常常使用GZIP压缩技术来减少文件大小,减少文件大小有两个明显的好处,一是可以减少存储空间,二是通过网络传输文件时,可以减少传输的时间.作者在写这篇博客时经过测试,4.4MB的文本数据经过Gzip传输到客户端之后变为392KB,压缩效率极高. 一.服务端 服务端有2种方式去压缩,一种可以自己压缩,但是更推荐第二种方式,用PrintWrite

  • Android实现下载zip压缩文件并解压的方法(附源码)

    前言 其实在网上有很多介绍下载文件或者解压zip文件的文章,但是两者结合的不多,所以这篇文章在此记录一下下载zip文件并直接解压的方法,直接上代码,文末有源码下载. 下载: import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream

  • Android zip文件下载和解压实例

    下载:DownLoaderTask.java 复制代码 代码如下: package com.johnny.testzipanddownload; import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IO

  • Android实现zip文件压缩及解压缩的方法

    本文实例讲述了Android实现zip文件压缩及解压缩的方法.分享给大家供大家参考.具体如下: DirTraversal.java如下: package com.once; import java.io.File; import java.util.ArrayList; import java.util.LinkedList; /** * 文件夹遍历 * @author once * */ public class DirTraversal { //no recursion public sta

  • Android中实现下载和解压zip文件功能代码分享

    本文提供了2段Android代码,实现了从Android客户端下载ZIP文件并且实现ZIP文件的解压功能,非常实用,有需要的Android开发者可以尝试一下. 下载: DownLoaderTask.java 复制代码 代码如下: package com.johnny.testzipanddownload; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; im

  • 仿墨迹天气在Android App中实现自定义zip皮肤更换

    在这里谈一下墨迹天气的换肤实现方式,不过首先声明我只是通过反编译以及参考了一些网上其他资料的方式推测出的换肤原理, 在这里只供参考. 若大家有更好的方式, 欢迎交流. 墨迹天气下载的皮肤就是一个zip格式的压缩包,在应用的时候把皮肤资源释放到墨迹天气应用的目录下,更换皮肤时新的皮肤资源会替换掉老的皮肤资源每次加载的时候就是从手机硬盘上读取图片,这些图片资源的命名和程序中的资源的命名保持一致,一旦找不到这些资源,可以选择到系统默认中查找.这种实现是直接读取了外部资源文件,在程序运行时通过代码显示的

  • Android App中进行语言的切换

    本篇简单介绍将在Android App中进行语言的切换和使用dragonFace改系统语言. 切换语言 首先需要在res 中创建个若干个不同的value文件夹(例如:values.values-en.value-ja).然后将不同的String.xml文件. 这里为 中.英.日三语切换.(value文件夹命名可以参考下面) 在res目錄下建立不同名稱的values文件來調用不同的語言包 Values文件匯總如下: 中文(中國):values-zh-rCN中文(台灣):values-zh-rTW

  • 如何在Android App中接入微信支付

    本篇简单介绍Android App中接入微信支付,包括App内支付和扫码支付.分享+支付 pofei 微信支付 wechat 官方接入文档 App内支付 源码下载 主要流程: 1.微信支付平台注册账号​ 注:注册并申请成功以后,需要在API安全中设置你的API密钥 32个字符.建议使用 MD5加密 ,并且需要妥善的保存.因为无法查看. 2.生成预支付订单 3.生成签名参数 4.调起微信,完成支付 扫码支付 扫码支付使用的是微信统一下单API ,使用的是模式二,模式一 一直说URL参数错误,完全按

  • Android开发中MJRefresh自定义刷新动画效果

    [一]常见用法 最原始的用法,耦合度低,但是不能统一管理.我们需要在每一个控制器都写以下代码,很繁琐,以后项目修改起来更繁琐,得一个控制器一个控制器的去定位.修改. 1.1 使用默认刷新(耦合度底,但是想统一修改起来特别麻烦) self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{ //在这里执行刷新操作 }]; self.tableView.mj_header = [MJRefreshNorm

  • Android App中ListView仿QQ实现滑动删除效果的要点解析

    本来准备在ListView的每个Item的布局上设置一个隐藏的Button,当滑动的时候显示.但是因为每次只要存在一个Button,发现每个Item上的Button相互间不好控制.所以决定继承ListView然后结合PopupWindow. 首先是布局文件: delete_btn.xml:这里只需要一个Button <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android=

  • 分析Android App中内置换肤功能的实现方式

    Android平台api没有特意为换肤提供一套简便的机制,这可能是外国的软件更注重功能和易用,不流行换肤.系统不提供直接支持,只能自行研究. 换肤,可以认为是动态替换资源(文字.颜色.字体大小.图片.布局文件--).这个使用编程语言来动态设置是可以做到的,例如使用View的setBackgroundResource.setTextSize.setTextColor等函数.但我们不可能在每个activity里对页面里的所有控件都通过调用这些函数来换肤,这样的程序代码难以维护.扩展,也违背了UI和代

  • 如何在Android App中集成支付宝和微信支付功能

    前言 本文主要介绍如何在 Android App 里集成支付宝和微信支付的功能,文中将实现的步骤一步步介绍的非常详细,对同样遇到这个问题的朋友相信会是一个很好的参考,下面话不多说了,来一起看看详细的介绍吧. 集成支付宝支付 没想到现在 App 里集成支付宝是这么的简单,我还折腾了好久- 好了,开始,假设你已经完成了支付宝那些繁杂的申请啥的工作,进入开发了. 首先,去下载官方的 DEMO : App支付客户端DEMO&SDK. 导入开发资源 解压后把里面的 jar 包拿出来放到你工程的 lib 目

  • 通过实例简单讲解Android App中的Activity组件

    Activity是Android应用中,最直接与用户接触的组件,它负责加载View组件,使其展现给用户,并保持与用户的交互.所有的Activity组件均需要继承Activity类,这是一个Content的间接子类,包装了一些Activity的基本特性. View组件是所有UI组件.容器组件的基类,也就是说,它可以是一个布局容器,也可以是一个布局容器内的基本UI组件.View组件一般通过XML布局资源文件定义,同时Android系统也对这些View组件提供了对应的实现类.如果需要通过某个Activ

  • Android App中各种数据保存方式的使用实例总结

    少量数据保存之SharedPreferences接口实例 SharedPreferences数据保存主要是通过键值的方式存储在xml文件中 xml文件在data/此程序的包名/XX.xml. 格式: <?xml version='1.0' encoding='utf-8' standalone='yes' ?> <map> <int name="count" value="3" /> <string name="t

  • Android App中实现可以双击放大和缩小图片功能的实例

    先来看一个很简单的核心图片缩放方法: public static Bitmap scale(Bitmap bitmap, float scaleWidth, float scaleHeight) { int width = bitmap.getWidth(); int height = bitmap.getHeight(); Matrix matrix = new Matrix(); matrix.postScale(scaleWidth, scaleHeight); Log.i(TAG, "s

随机推荐