仿墨跡天氣在Android App中實現(xiàn)自定義zip皮膚更換
在這里談一下墨跡天氣的換膚實現(xiàn)方式,不過首先聲明我只是通過反編譯以及參考了一些網(wǎng)上其他資料的方式推測出的換膚原理, 在這里只供參考. 若大家有更好的方式, 歡迎交流.
墨跡天氣下載的皮膚就是一個zip格式的壓縮包,在應用的時候把皮膚資源釋放到墨跡天氣應用的目錄下,更換皮膚時新的皮膚資源會替換掉老的皮膚資源每次加載的時候就是從手機硬盤上讀取圖片,這些圖片資源的命名和程序中的資源的命名保持一致,一旦找不到這些資源,可以選擇到系統(tǒng)默認中查找。這種實現(xiàn)是直接讀取了外部資源文件,在程序運行時通過代碼顯示的替換界面的背景資源。這種方式的優(yōu)點是:皮膚資源的格式定義很隨意可以是zip也可以是自定義的格式,只要程序中能夠解析到資源就行,缺點是效率上的問題.
這里需要注意的一點是,再這里對壓縮包的解壓,借助了第三方工具: ant. jar進行解壓和壓縮文件. 關于ant工具的使用,我在稍后的文章中會具體介紹.
主要技術點:
如何去讀取zip文件中的資源以及皮膚文件存放方式
實現(xiàn)方案:如果軟件每次啟動都去讀取SD卡上的皮膚文件,速度會比較慢。較好的做法是提供一個皮膚設置的界面,用戶選擇了哪一個皮膚,就把那個皮膚文件解壓縮到”/data/data/[package name]/skin”路徑下(讀取的快速及安全性),這樣不需要跨存儲器讀取,速度較快,而且不需要每次都去zip壓縮包中讀取,不依賴SD卡中的文件,即使皮膚壓縮包文件被刪除了也沒有關系。
實現(xiàn)方法:
1. 在軟件的幫助或者官網(wǎng)的幫助中提示用戶將皮膚文件拷貝到SD卡指定路徑下。
2. 在軟件中提供皮膚設置界面。可以在菜單或者在設置中??蓞⒖寄E、搜狗輸入法、QQ等支持換膚的軟件。
3. 加載指定路徑下的皮膚文件,讀取其中的縮略圖,在皮膚設置界面中顯示,將用戶選中的皮膚文件解壓縮到”/data/data/[package name]/skin”路徑下。
4. 軟件中優(yōu)先讀取”/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調(diào)用,遞歸完成目錄文件讀取
* @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) {// 如果目錄為空,則單獨創(chuàng)建之.
// 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 {
// 如果指定文件的目錄不存在,則創(chuàng)建之.
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();
}
}
// 設置緩沖區(qū)大小
public void setBufSize(int bufSize) {
this.bufSize = bufSize;
}
}
- Android實現(xiàn)zip文件壓縮及解壓縮的方法
- Android實現(xiàn)下載zip壓縮文件并解壓的方法(附源碼)
- Android GZip的使用-開發(fā)中網(wǎng)絡請求的壓縮實例詳解
- Android Zipalign工具優(yōu)化Android APK應用
- Android APK優(yōu)化工具Zipalign詳解
- Android中實現(xiàn)下載和解壓zip文件功能代碼分享
- Android zip文件下載和解壓實例
- 在Android系統(tǒng)中使用gzip進行數(shù)據(jù)傳遞實例代碼
- Android RxJava創(chuàng)建操作符Interval
相關文章
Android 中自定義ContentProvider與ContentObserver的使用簡單實例
這篇文章主要介紹了Android 中自定義ContentProvider與ContentObserver的使用簡單實例的相關資料,這里提供實例幫助大家學習理解這部分內(nèi)容,需要的朋友可以參考下2017-09-09
Android開發(fā)獲取手機Mac地址適配所有Android版本
這篇文章主要介紹了Android開發(fā)獲取手機Mac地址適配所有Android版本,需要的朋友可以參考下2020-03-03
Android sharedPreferences實現(xiàn)記住密碼功能
這篇文章主要為大家詳細介紹了Android sharedPreferences實現(xiàn)記住密碼功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-04-04
android viewpager實現(xiàn)豎直滑動效果
這篇文章主要為大家詳細介紹了android viewpager實現(xiàn)豎直滑動效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-07-07
Android開發(fā)應用第一步 安裝及配置模擬器Genymotion
這篇文章主要介紹了Android開發(fā)應用第一步,即安裝及配置模擬器Genymotion,感興趣的小伙伴們可以參考一下2015-12-12

