編輯:關於Android編程
相信去年聖誕節打開過手機淘寶的童鞋都會對當時的特效記憶猶新吧:全屏飄雪,旁邊還有個小雪人來控制八音盒背景音樂的播放,讓人有種身臨其境的感覺,甚至忍不住想狠狠購物了呢(誤),大概就是下面這個樣子滴:
嗯,確實很炫,那麼我們一步步去分析是如何實現的:
一、實現下雪的 View
首先,最上面一層的全屏雪花極有可能是一個頂層的View,而這個View是通過動態加載去控制顯示的(不更新淘寶也能看到這個效果)。那麼我們先得實現雪花效果的 View,人生苦短,拿來就用。打開 gank.io,搜索"雪花":
看樣子第7個庫就是我們想要的了,點進源碼,直接 download 不解釋,記得 star 一個支持作者。那麼現在我們的項目中就有一個完整的下雪效果 View 了。
二、實現雪人播放器 View
這個一張雪人圖片+一個按鈕即可實現,就不多解釋了。接下來需要一段聖誕節音頻,直接進行在線音頻播放無疑是節省空間的好方案。『我的滑板鞋』烘托出的寂寞而甜蜜的氛圍無疑是最適合聖誕節的,因此我們得到了『神曲』URL 一枚:
http://cdn.ifancc.com/TomaToDo/bgms/my_hbx.mp3
接下來要找一個小雪人的圖片當作播放器的背景,那麼阿姆斯特朗...不對,是這個:
嗯,相當可愛喜慶。那麼播放器核心代碼如下:
package com.kot32.christmasview.player; import android.content.Context; import android.media.AudioManager; import android.media.MediaPlayer; import android.util.AttributeSet; import android.view.View; import android.widget.Toast; import com.kot32.christmasview.R; import java.io.IOException; /** * Created by kot32 on 16/12/8. */ public class MyPlayer extends View { public MediaPlayer mediaPlayer; public MyPlayer(Context context) { super(context); init(); } public MyPlayer(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { setBackgroundResource(R.drawable.pig); mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); playUrl("http://172.20.248.106/IXC5b415fcacfc3c439e25a3e74533d2239/TomaToDo/bgms/my_hbx.mp3"); Toast.makeText(getContext(), "開始播放", Toast.LENGTH_SHORT).show(); setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (!mediaPlayer.isPlaying()) { mediaPlayer.start(); Toast.makeText(getContext(), "繼續播放", Toast.LENGTH_SHORT).show(); } else { mediaPlayer.pause(); Toast.makeText(getContext(), "暫停播放", Toast.LENGTH_SHORT).show(); } } }); } public void playUrl(String videoUrl) { try { mediaPlayer.reset(); mediaPlayer.setDataSource(videoUrl); mediaPlayer.prepare();//prepare之後自動播放 mediaPlayer.start(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); try { mediaPlayer.stop(); mediaPlayer.release(); }catch (Exception e){ e.printStackTrace(); } } }
三、動態加載思路
上面基本實現了在本地的雪花以及播放音樂效果,那麼在不更新主程序的情況下,如何將這兩個View動態加載到主程序當中去呢?
首先我們明白,Android 的DexClassloader 是擁有加載任意APK 中任意類的能力的,只是有以下限制:
加載出的Activity 由於不在宿主 Manifest 文件中聲明,因此框架無法找到並初始化這個Activity。
加載出的Activity 不具備生命周期,理由同上。
加載出的類的Resource 文件id 會和主程序混淆在一起。
由於我們只是加載View,並不是加載整個Activity,所以前兩個問題並不會遇到,而第三個問題可以想辦法解決掉。
在主程序中我們也要做這三件事:
把能夠裝載View的ViewGroup 的空位留出來
去獲取更新的patch包
把View 從apk包中加載出來之後,放進留好的ViewGroup 中。
這樣一來,不僅是聖誕節,在之後的各種活動上都可以在線去加載活動的View。
四、開始加載
在加載View 之前,首先要意識到這個View 是引用了圖片資源的(小豬圖片),因此我們要解決資源問題:
private void initResource() { Resources resources = getContext().getResources(); try { AssetManager newManager = AssetManager.class.newInstance(); Method addAssetPath = newManager.getClass().getMethod("addAssetPath", String.class); addAssetPath.invoke(newManager, DynamicViewManager.getInstance().getUpdateFileFullPath()); Resources newResources = new Resources(newManager, resources.getDisplayMetrics(), resources.getConfiguration()); Reflect.onObject(getContext()).set("mResources", newResources); } catch (Exception e) { e.printStackTrace(); } }
上面代碼的作用是:把添加了外部更新包路徑的資源管理器賦值給了App原來的資源管理器,也就是說現在可以在宿主中訪問插件資源了。
核心加載代碼如下:
DexClassLoader classLoader = new DexClassLoader(apkFile.getAbsolutePath() , "dex_out_put_dir" , null , getClass().getClassLoader()); Class newViewClazz = classLoader.loadClass("view's package name"); Constructor con = newViewClazz.getConstructor(Context.class); //first use Activity's Resource lie to View if (dynamicView == null) { dynamicView = (View) con.newInstance(getContext()); } //Replace the View's mResources and recovery the Activity's avoid disorder of Resources Reflect.onObject(getContext()).set("mResources", null); getContext().getResources(); RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(DisplayUtil.dip2px(getContext(), viewInfo.layoutParams.width), DisplayUtil.dip2px(getContext(), viewInfo.layoutParams.height)); layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE); addView(dynamicView, layoutParams);
中間對 mResources 的操作的作用是:將宿主的Activity 的mResources 重置,避免在Activity 中使用資源時和插件沖突。
然而機智的我已經把更新包下載、版本管理、動態加載都封裝好了,所以正確的加載方式是:
引用它:https://github.com/kot32go/dynamic-load-view
然後:
1.宿主聲明:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/tb_bg" > <com.kot32.dynamicloadviewlibrary.core.DynamicViewGroup android:layout_width="match_parent" android:layout_height="match_parent" app:uuid="activity_frame"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="原始頁面" /> </com.kot32.dynamicloadviewlibrary.core.DynamicViewGroup> <com.kot32.dynamicloadviewlibrary.core.DynamicViewGroup android:layout_width="60dp" android:layout_height="60dp" android:layout_alignParentRight="true" android:layout_centerVertical="true" app:uuid="activity_player"> </com.kot32.dynamicloadviewlibrary.core.DynamicViewGroup> </RelativeLayout>
以上聲明了主界面的布局,當然,在動態加載之前除了原有的"原始頁面"TextView,是不會有任何其他東西的,也就是聖誕節來臨之前的程序。注意:uuid 會和在線包相匹配。
2.打插件包
其實就是把之前包含了我們所寫的兩個View(雪花和雪人)的程序打包成apk。可以不簽名。
3.把插件包放到服務器
在服務器返回的JSON中聲明插件包地址和動態View 的一些參數,這裡的演示程序請求地址為:
http://tomatodo.ifancc.com/php/dynamicView.php
返回值為:
{ "version": 54, "downLoadPath": "http://obfgb7oet.bkt.clouddn.com/patch106.apk", "fileName": "patch106.apk", "viewInfo": [ { "packageName": "com.kot32.testdynamicviewproject.snow.widgets.SnowingView", "uuid": "activity_frame", "layoutParams": { "width": -1, "height": -1 } }, { "packageName": "com.kot32.testdynamicviewproject.player.MyPlayer", "uuid": "activity_player", "layoutParams": { "width": -1, "height": -1 } } ] }
我們聲明了這次在線包的版本,每個View 的包名和布局參數, 以及最重要的 和宿主程序中聲明對齊的uuid。
以上所述是小編給大家介紹的Android中利用動態加載實現手機淘寶的節日特效,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對本站網站的支持!
對於Android 的手勢不光在軟件中會經常用到,比如浏覽器中的翻頁,滾動頁面等等;當然其實在我
最近公司項目需要,要做一個自己的IMSDK,順便先把之前沒有記錄的群聊功能記錄一下。先上資料,查看XMPP群聊相關的資料,可以去這裡看協議:XEP-0045 。創建群組X
這篇我們來介紹一下享元模式(Flyweight Pattern),Flyweight 代表輕量級的意思,享元模式是對象池的一種實現。享元模式用來盡可能減少內存使用量,它適
前提:手機已經root; 1.手機連接電腦,打開Cmd,運行命令adb shell;//因為android用的Linux內核,很多linux的命令,在Android也可以