[천장] 안드로이드 스킨 교체(테마) 용이
제가 몇 가지 앱 스킨케어 앱을 찾았는데 스킨케어는 기본적으로 인터페이스의 Icon, 배경 이미지, 배경색 등을 바꿨습니다. 기본적으로 레이아웃을 바꾸는 것을 만나지 못했습니다. 사실 레이아웃도 바꿀 수 있지만 필요 없다고 생각합니다.그래서 이 글에서 설명한 스킨케어도 아이콘, 배경그림 등 자원을 바꾸는 것을 가리킨다.
인터넷 검색을 통해 나는 인터넷에 대략 이렇게 집중적인 스킨케어 메커니즘을 제공한 것을 발견했다.
1. 피부팩을 apk에 직접 넣는 방안은 매우 간단하지만 유연성이 부족하고 apk를 크게 만든다.
2. 피부를 하나의 독립된 apk 파일로 만들고 프로젝트 프로젝트와shareUsedId를 공용하며 같은 서명을 가진다.이런 방안은 첫 번째 방안에 비해 유연성이 비교적 크고 단점은 사용자의 설치가 필요하다는 것이다. 시나닷컴이 현재 사용하고 있는 것이 바로 이런 방안이다.
내가 오늘 소개하고자 하는 이런 방안은 두 번째 방안과 비교적 유사하지만 나의 자원 패키지는 설치하지 마라. 왜냐하면 사용자들은 일반적으로 엉망진창인 응용을 설치하기를 원하기 때문이다.
이 글을 배우기 전에 제 전 글인 안드로이드 자원 관리 메커니즘 분석을 배우는 것이 좋습니다. 피부 관리는 사실 자원 관리이기 때문입니다.이제 피부를 바꾸는 법을 배워보도록 하겠습니다.
1. 우선 우리는 스킨백을 준비해야 한다. 이 스킨백에는 어떠한 Activity도 포함되지 않고 그 안에 자원 파일만 있다. 여기에 나는 간단하게 하기 위해 하나의 color만 추가한다.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="main_btn_color">#E61ABD</color>
<color name="main_background">#38F709</color>
<color name="second_btn_color">#000000</color>
<color name="second_background">#FFFFFF</color>
</resources>
2. 이 자원을 apk 파일로 포장하여 sd카드에 넣는다(실제 항목은 내 네트워크에서 다운로드할 수 있다)
3. 스킨케어가 필요한Activity를 ISkinUpdate(이것은 스스로 이름을 정의할 수 있음) 인터페이스로 구현
public class MainActivity extends Activity implements ISkinUpdate,OnClickListener
{
private Button btn_main;
private View main_view;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_main);
SkinApplication.getInstance().mActivitys.add(this);
btn_main=(Button)this.findViewById(R.id.btn_main);
btn_main.setOnClickListener(this);
main_view=this.findViewById(R.id.main_view);
}
@Override
protected void onResume() {
super.onResume();
if(SkinPackageManager.getInstance(this).mResources!=null)
{
updateTheme();
Log.d("yzy", "onResume-->updateTheme");
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
//Toast.makeText(this, "change skin", 1000).show();
File dir=new File(Environment.getExternalStorageDirectory(),"plugins");
File skin=new File(dir,"SkinPlugin.apk");
if(skin.exists())
{
SkinPackageManager.getInstance(MainActivity.this).loadSkinAsync(skin.getAbsolutePath(), new loadSkinCallBack() {
@Override
public void startloadSkin()
{
Log.d("yzy", "startloadSkin");
}
@Override
public void loadSkinSuccess() {
Log.d("yzy", "loadSkinSuccess");
MainActivity.this.sendBroadcast(new Intent(SkinBroadCastReceiver.SKIN_ACTION));
}
@Override
public void loadSkinFail() {
Log.d("yzy", "loadSkinFail");
}
});
}
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void updateTheme()
{
// TODO Auto-generated method stub
if(btn_main!=null)
{
try {
Resources mResource=SkinPackageManager.getInstance(this).mResources;
Log.d("yzy", "start and mResource is null-->"+(mResource==null));
int id1=mResource.getIdentifier("main_btn_color", "color", "com.skin.plugin");
btn_main.setBackgroundColor(mResource.getColor(id1));
int id2=mResource.getIdentifier("main_background", "color","com.skin.plugin");
main_view.setBackgroundColor(mResource.getColor(id2));
//img_skin.setImageDrawable(mResource.getDrawable(mResource.getIdentifier("skin", "drawable","com.skin.plugin")));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
SkinApplication.getInstance().mActivitys.remove(this);
super.onDestroy();
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(v.getId()==R.id.btn_main)
{
Intent intent=new Intent(this,SecondActivity.class);
this.startActivity(intent);
}
}
}
이 코드는 주로 onOptions Item Selected를 보는데 이 방법에서 자원 apk 경로를 통해 이 자원 apk가 Resources 대상에 대응하는 것을 얻을 수 있다.SkinPacakge Manager에서 뭘 했는지 바로 만나보도록 하겠습니다.
/**
*
* com.skin.demo.SkinPackageManager
* @author yuanzeyao <br/>
* create at 2015 1 3 3:24:16
*/
public class SkinPackageManager
{
private static SkinPackageManager mInstance;
private Context mContext;
/**
*
*/
public String mPackageName;
/**
*
*/
public Resources mResources;
private SkinPackageManager(Context mContext)
{
this.mContext=mContext;
}
public static SkinPackageManager getInstance(Context mContext)
{
if(mInstance==null)
{
mInstance=new SkinPackageManager(mContext);
}
return mInstance;
}
/**
*
* @param dexPath
*
* @param callback
*
*/
public void loadSkinAsync(String dexPath,final loadSkinCallBack callback)
{
new AsyncTask<String,Void,Resources>()
{
protected void onPreExecute()
{
if(callback!=null)
{
callback.startloadSkin();
}
};
@Override
protected Resources doInBackground(String... params)
{
try {
if(params.length==1)
{
String dexPath_tmp=params[0];
PackageManager mPm=mContext.getPackageManager();
PackageInfo mInfo=mPm.getPackageArchiveInfo(dexPath_tmp,PackageManager.GET_ACTIVITIES);
mPackageName=mInfo.packageName;
AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
addAssetPath.invoke(assetManager, dexPath_tmp);
Resources superRes = mContext.getResources();
Resources skinResource=new Resources(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration());
SkinConfig.getInstance(mContext).setSkinResourcePath(dexPath_tmp);
return skinResource;
}
return null;
} catch (Exception e) {
return null;
}
};
protected void onPostExecute(Resources result)
{
mResources=result;
if(callback!=null)
{
if(mResources!=null)
{
callback.loadSkinSuccess();
}else
{
callback.loadSkinFail();
}
}
};
}.execute(dexPath);
}
/**
*
* com.skin.demo.loadSkinCallBack
* @author yuanzeyao <br/>
* create at 2015 1 4 1:45:48
*/
public static interface loadSkinCallBack
{
public void startloadSkin();
public void loadSkinSuccess();
public void loadSkinFail();
}
}
loadSkin Async를 호출한 후 성공하면 스킨케어 방송을 보내고 현재 피부 apk의 경로를 sp에 저장하여 다음 앱을 시작하면 이 피부 자원을 직접 불러올 수 있도록 합니다.스킨케어 수락 방송은 스킨케어에 등록된 것으로 이 방송을 받은 후 이미 시작되고 스킨케어가 필요한 Activity의 업데이트 Theme 방법을 모두 호출하여 스킨케어를 실현한다.
public class SkinApplication extends Application
{
private static SkinApplication mInstance=null;
public ArrayList<ISkinUpdate> mActivitys=new ArrayList<ISkinUpdate>();
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
mInstance=this;
String skinPath=SkinConfig.getInstance(this).getSkinResourcePath();
if(!TextUtils.isEmpty(skinPath))
{
// , ,
SkinPackageManager.getInstance(this).loadSkinAsync(skinPath, null);
}
SkinBroadCastReceiver.registerBroadCastReceiver(this);
}
public static SkinApplication getInstance()
{
return mInstance;
}
@Override
public void onTerminate() {
// TODO Auto-generated method stub
SkinBroadCastReceiver.unregisterBroadCastReceiver(this);
super.onTerminate();
}
public void changeSkin()
{
for(ISkinUpdate skin:mActivitys)
{
skin.updateTheme();
}
}
}
여기서 스킨케어는 아이콘, 배경색 등만 바꾸는 것이기 때문에 비교적 간단합니다. 만약에 레이아웃 파일을 바꾸려면 좀 복잡해야 합니다. 여기는 더 이상 소개하지 않겠습니다. 관심 있는 사람은 스스로 연구할 수 있습니다...
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.