標籤:android 自訂gifview 反射
之前寫了一篇部落格,《【Android實戰】記錄自學自訂GifView過程,詳解屬性那些事!【學習篇】》
關於自訂GifView的,詳細講解了學習過程及遇到的一些類的解釋,然後完成了一個項目,能通過在xml加入自訂 view (MyGifView)中加入自訂屬性(my:gif_src = “@drawable/coffee”),達到播放gif圖片的效果。
但是,有幾個問題
1.gif_src 屬性只支援 gif 圖,並不支援其他類型的圖片
2.只支援預設的引用圖片,不能另外設定
問題一
gif_src 屬性只支援 gif 圖,並不支援其他類型的圖片。
解決思路:
ImageView本身有個屬性 src 是定義好的,已經可以用它播放靜態圖片,如果再能通過它播放動態圖片,不就解決問題啦?!
於是查看 ImageView 類的源碼,看到建構函式
public ImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { //... final TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.ImageView, defStyleAttr, defStyleRes); Drawable d = a.getDrawable(com.android.internal.R.styleable.ImageView_src); if (d != null) { setImageDrawable(d); } //...}
有沒有很眼熟?!對,之前自訂屬性的時候用過!這裡不過把屬性路徑改了!之前我們用的是自訂的路徑 R.styleable.GifView 。
於是乎,我也想著,要是能在繼承類(MyGifView)裡面複製上段代碼,然後再用movie轉化,轉換成功說明是 gif,就用之前的方法播放,轉換失敗說明是其他格式的圖片,就交給 ImageView 自己處理!
真是好辦法!
然而,根本不能這麼用:
但是思路是對的!
通過參考《 Android PowerImageView實現,可以播放動畫的強大ImageView》
得知了可以用反射!
中心代碼:
/** * 通過Java反射,擷取到src指定圖片資源所對應的id。 * * @param a 屬性群組 * @param context * @return 返回布局檔案中指定圖片資源所對應的id,沒有指定任何圖片資源就返回0。 */ private int getResourceId(TypedArray a, Context context) { try { Field field = TypedArray.class.getDeclaredField("mValue"); field.setAccessible(true); TypedValue typedValueObject = (TypedValue) field.get(a); return typedValueObject.resourceId; } catch (Exception e) { e.printStackTrace(); } finally { if (a != null) { a.recycle(); } } return 0; }
之前在自訂view初始化中的代碼,我是用得到自訂屬性值的方法擷取gif的資料
int resId = typedArray.getResourceId(R.styleable.GifView_gif_src, 0); //gif_src屬性對應值
現在只需要改這一句就好啦!
//int resId = typedArray.getResourceId(R.styleable.GifView_gif_src, 0); //gif_src屬性對應值int resId = getResourceId(typedArray, context); //src屬性對應值
然後後面都不用改啦!
(但是轉換成 InputStream 的時候,還是要加一句判斷 if (resId != 0)再進行轉換)
if (resId != 0) { InputStream iStream = getResources().openRawResource(resId); //此方法能通過資源檔id尋找到資源檔並轉化為輸入資料流 mMovie = Movie.decodeStream(iStream); //輸入資料流轉化為Movie (mMovie 為全域變數,類型 Movie)}
問題二
只支援預設的引用圖片,不能另外設定
解決思路:
從外面設定無非就是外面調用setImageResource(int resId),setImageDrawable(Drawable drawable),setImageBitmap(Bitmap bm)等這些方法去改變 ImageView 屬性 src 所對應的值!
那麼,重寫這些方法,把資源改成我們的 movie 就好啦!so easy!
首先重寫setImageResource(int resId)
@Override public void setImageResource(int resId) { if (resId != 0) { InputStream iStream = getResources().openRawResource(resId); Movie movie = Movie.decodeStream(iStream); setMovie(movie, iStream); if (mMovie == null) { super.setImageResource(resId); } } else { super.setImageResource(resId); } invalidate();}
然後在外面(比如MainActivity),調用gifView.setImageResource(R.drawable.coffee)是可以顯示gif的,其他格式的圖片也可以正常顯示。
but…
出現了一個bug…
就是現在必須在xml裡面的自訂MyGifView添加預設的 src 引用 或者 backgroud 附初始值,不然會報錯崩潰,如果不想添加預設圖片,可以把background設定為透明 #00000000
報錯的原因,大概是沒設定src屬性時,調用反射int resId = getResourceId(typedArray, context);得到的 resId 也並不為0 (具體得到的是什麼我也還不知),然後進入 if 語句執行InputStream iStream = getResources().openRawResource(resId);轉換流的時候報了null 指標,導致程式崩潰。
暫時按照設定預設src或者backgroud的方法可以解決,如果廣大網友知道是什麼原因,有什麼更好的辦法解決它,懇求告知一下!
代耕……
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
【Android實戰】記錄自學自訂GifView過程,能同時支援gif和其他圖片!【實用篇】