1. 程式人生 > >Android 顯示 Gif動態圖片的三種常用方法

Android 顯示 Gif動態圖片的三種常用方法

1.首先描述下自定義控制元件的一般方法:

自定義控制元件是被創造出來,所以先要複寫它三個的構造方法,根據需求決定複寫哪個,若沒有自定義屬性,複寫只有一個引數的即可。若有自定義屬性,則可以通過obtainStyledAttributes獲得TypedArray物件,通過該物件獲得屬性並進行相應操作。然後下面的過程和畫畫差不多,我們在畫一樣東西的時候,首先要知道物品大概的樣子,也就是需要複寫onMeasure()方法,測量控制元件以及其子佈局的大小。知道物品的大概後,我們需要對其裡面的細節精雕細琢,也就是呼叫onLayout()方法,決定其子View的位置。最後就是將圖整體勾勒出來,即是呼叫onDraw()方法。這樣子控制元件大概的樣子就出來了,再根據需求增加相應藉口,回撥方法。

2.Android顯示gif的三種方式:

(1)使用Android自帶的Movie物件,將Gif當成視訊。隨著時間的變化,通過setTime方法設定需要顯示的圖片,然後onDraw方法不斷將新的畫畫出來。例項程式碼如下:

MainActivity:

public class MainActivity extends Activity {

	
	private GifView gv;
	private ListView lv_content;
	private int[] gifResource={R.drawable.car,R.drawable.rick,R.drawable.car,R.drawable.mm,R.drawable.qq};
	private MyAdapter adapter=null;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		lv_content=(ListView) findViewById(R.id.lv_content);

		adapter = new MyAdapter();
		lv_content.setAdapter(adapter);
	/*	
		lv_content.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				
				if(adapter!=null)
				{
					adapter.notifyDataSetChanged();
				}
			}
		});*/
	
		// 去掉listView的item點選的效果
//		lv_content.setEnabled(false);
		lv_content.setSelector(color.transparent);
	}
	
	
	private class MyAdapter extends BaseAdapter{

		@Override
		public int getCount() {
			// TODO Auto-generated method stub
			return gifResource.length;
		}

		@Override
		public Object getItem(int position) {
			return null;
		}

		@Override
		public long getItemId(int position) {
			return 0;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			
			ViewHolder holder=null;
			if(convertView == null)
			{
				convertView=View.inflate(getApplicationContext(), R.layout.item_gif, null);
				holder=new ViewHolder();
				holder.tv_username=(TextView) convertView.findViewById(R.id.tv_username);
				holder.tv_comemnt=(TextView) convertView.findViewById(R.id.tv_comment);
				holder.gv=(GifView) convertView.findViewById(R.id.gv);
				
				convertView.setTag(holder);
			}else{
				
				holder=(ViewHolder) convertView.getTag();
			}
			
			holder.tv_username.setText("username"+position);
			holder.tv_comemnt.setText("comment"+position);
			holder.gv.setMovieResource(gifResource[position]);
			
			holder.gv.setOnClickListener(new OnClickListener() {
				
				@Override
				public void onClick(View v) {
					GifView view=(GifView) v;
					view.setPaused(!view.isPause());
				}
			});
			
			return convertView;
		}
		
		
		class ViewHolder{
			
			public TextView tv_username;
			public TextView tv_comemnt;
			public GifView gv;
			
			
		}
		
	}

	
	
}

自定義元件GifView:

package com.example.gifdemo.view;

import com.example.gifdemo.R;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Movie;
import android.os.Build;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.View;


/**使用Android自定義的movie實現**/

public class GifView extends View{

	
	private static final int DEFAULT_GIF_DURATION=1000;
	// the reference for gif
	private int mMovieResourceId;
	private Movie mMovie;
	
	
	private long mStart;
	private int mCurrentAnimationTime=0;
	
	private boolean mPause=true;
	private boolean mVisible=true;
	
	
	private long mWidth;
	private long mHeight;
	
	private float mScale;
	
	private float mLeft;
	private float mTop;
	
	private int mMeasuredWidth;
	private int mMeasureHeight;
	
	public GifView(Context context) {
		this(context,null);
	}
	
	
	public GifView(Context context, AttributeSet attrs) {
		this(context, attrs,R.styleable.CustomTheme_gifMoviewViewStyle);
	}
	
	
	public GifView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		setViewAttributes(context, attrs, defStyleAttr);
	}
	
	
	private void setViewAttributes(Context context, AttributeSet attrs, int defStyleAttr)
	{
		
		/**
		 * Starting from HONEYCOMB have to turn off HW acceleration to draw
		 * Movie on Canvas.
		 */
		if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB)
		{
			setLayerType(View.LAYER_TYPE_SOFTWARE, null);
		}
		
		//?? TODO get the collection of the attrs
		final TypedArray array=context.obtainStyledAttributes(attrs, R.styleable.GifView, defStyleAttr, R.style.Widget_GifMoviewView);
		
		mPause=array.getBoolean(R.styleable.GifView_paused, true);
		mMovieResourceId=array.getResourceId(R.styleable.GifView_gif, -1);
		
		// recycle TypedArray
		array.recycle();
		
		if(mMovieResourceId!=-1)
		{
			mMovie=Movie.decodeStream(getResources().openRawResource(mMovieResourceId));
		}
		
	}
	
	
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		
		if(mMovie!=null)
		{
			
			int mWidth=mMovie.width();
			int mHeight=mMovie.height();
			
		
		float scaleX=1f;
		int measureModeWidth=MeasureSpec.getMode(widthMeasureSpec);
		if(measureModeWidth!=MeasureSpec.UNSPECIFIED)
		{
			int maximumWidth=MeasureSpec.getSize(widthMeasureSpec);
			if(mWidth>maximumWidth)
			{
				scaleX=(float)mWidth/(float)maximumWidth;
			}
		}
		
		
		float scaleY=1f;
		int measureModeHeight=MeasureSpec.getMode(heightMeasureSpec);
		if(measureModeHeight!=MeasureSpec.UNSPECIFIED)
		{
			int maximumHeight=MeasureSpec.getSize(heightMeasureSpec);
			if(mHeight>maximumHeight)
			{
				scaleY=(float)mHeight/(float)maximumHeight;
			}
		}
		
		
		mScale=1f/Math.max(scaleX, scaleY);
		
		mMeasuredWidth=(int) (mWidth*mScale);
		mMeasureHeight=(int) (mHeight*mScale);
		
		setMeasuredDimension(mMeasuredWidth,mMeasureHeight);
		}else
		{
			setMeasuredDimension(getSuggestedMinimumWidth(), getSuggestedMinimumHeight());
			
		}
		
	}
	
	
	
	@Override
	protected void onLayout(boolean changed, int left, int top, int right,
			int bottom) {
		super.onLayout(changed, left, top, right, bottom);
		
		/** Calculate left / top for drawing in center **/
		
		mLeft=(getWidth()-mMeasuredWidth)/2f;
		mTop=(getHeight()-mMeasureHeight)/2f;
		
		mVisible= getVisibility() == View.VISIBLE;
	}
	
	
	@Override
	protected void onDraw(Canvas canvas) {
		
		if(mMovie!=null)
		{
			
			if(!mPause)
			{
				updateCurrentTime();
				drawFrame(canvas);
				invalidateView();
			}else{
				
				drawFrame(canvas);
			}
			
		}
	}
	
	
	private void updateCurrentTime()
	{
		long now=SystemClock.uptimeMillis();
		if(mStart==0)
		{
			mStart=now;
		}
		
		int dur=mMovie.duration();
		if(dur==0)
		{
			dur=DEFAULT_GIF_DURATION;
		}
		
		mCurrentAnimationTime=(int)((now-mStart)%dur);
		
	}
	
	private void drawFrame(Canvas canvas)
	{
		
		mMovie.setTime(mCurrentAnimationTime);
		canvas.save(Canvas.MATRIX_SAVE_FLAG);
		canvas.scale(mScale, mScale);
		// TODO
		mMovie.draw(canvas, mLeft/mScale, mTop/mScale);
		mMovie.draw(canvas, 0, 0);
		canvas.restore();
	}
	
	
	/**
	 * Invalidates view only if it is visible.
	 * <br>
	 * {@link #postInvalidateOnAnimation()} is used for Jelly Bean and higher.
	 * 
	 */
	@SuppressLint("NewApi")
	private void invalidateView() {
		if(mVisible) {
			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
				postInvalidateOnAnimation();
			} else {
				invalidate();
			}
		}
	}
	
	
	
	// the methods of the important attribute
	public void setPaused(boolean pause)
	{
		this.mPause=pause;
		
		// retrieve the StartTime
		if(!mPause)
		{
			mStart=SystemClock.uptimeMillis()-mCurrentAnimationTime;
		}
		
		invalidate();
	}
	
	
	public boolean isPause()
	{
		return mPause;
	}
	
	
	public void setMovieResource(int movieResourceId)
	{
		this.mMovieResourceId=movieResourceId;
		mMovie=Movie.decodeStream(getResources().openRawResource(movieResourceId));
		// important Call this when something has changed which has invalidated the
	    //layout of this view. 
		requestLayout();
	}
	
	public void setMovie(Movie movie)
	{
		this.mMovie=movie;
		requestLayout();
	}
	
	
	public Movie getMovie()
	{
		return mMovie;
	}
	
	
	public void setMovieTime(int time)
	{
		this.mCurrentAnimationTime=time;
		invalidate();
	}
}

(2)自定義TextView顯示Gif,先使用GifDecoder將Gif檔案解析成一張張圖片,然後通過ImageSpan將圖片顯示在TextView中,定義一個執行緒不斷重新整理圖片。

自定義TextView:

package com.yyg.gifdemo3.view;

public class MyTextView extends TextView{

	private Context mContext;
	private SpannableStringBuilder mSb=new SpannableStringBuilder();
	private String dummyStr="dummy";
	private SimpleImageMemCache mCache;
	private List<AnimatedImageSpan> mGifSpanList=new ArrayList<AnimatedImageSpan>();
	
	
	public MyTextView(Context context) {
		super(context);
		mContext=context;
	}
	
	public MyTextView(Context context, AttributeSet attrs) {
		super(context, attrs);
		mContext=context;
		
	}	
	
	public MyTextView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		mContext=context;
	}
	
	public void setImageCache(SimpleImageMemCache pImageCache)
	{
		this.mCache=pImageCache;
	}
	
	
	
	@Override
	protected void onDetachedFromWindow() {
		super.onDetachedFromWindow();
		Log.d(this.getClass().getName(), "onDetachedFromWindow ");
		for (AnimatedImageSpan ais : mGifSpanList) {
			Log.d(this.getClass().getName(), "animation playing " + ais.isPlaying());
			if (ais.isPlaying()) {
				ais.stopRendering();
			}
		}
		mGifSpanList.clear();
		mSb.clearSpans();
		mSb.clear();
	}
	

	public void appendText(String text)
	{
		mSb.append(text);
	}
	public void appendAnimation(GifResource pAssetsSet,AnimationSettings pSettings)
	{
		mSb.append(dummyStr);
		AnimatedImageSpan ais=new AnimatedImageSpan(mContext);
		ais.setAssetsSet(pAssetsSet);
		ais.setBitmapCache(mCache);
		mSb.setSpan(ais, mSb.length()-dummyStr.length(), mSb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
		AnimationClickableSpan clickableSpan=new AnimationClickableSpan(this, ais, pSettings);
		mSb.setSpan(clickableSpan, mSb.length()-dummyStr.length(), mSb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
		mGifSpanList.add(ais);
	}
	
	public void finishAddContent()
	{
		this.setText(mSb);
		// setMovementMethod,此方法在需要響應使用者事件時使用,如點選一個電話號碼就跳轉到撥號頁面。如果不執行這個方法是不會響應事件的,即便文字看著已經是下劃線藍色字了。
		this.setMovementMethod(LinkMovementMethod.getInstance());
	}
	
	private static class AnimationClickableSpan extends ClickableSpan
	{
		private AnimatedImageSpan mImageSpan;
		private AnimationSettings mSettings;
		private AnimatedImageUpdateHandler updateHandler;
		
		
		public AnimationClickableSpan(MyTextView pTextView ,AnimatedImageSpan pAnimatedImageSpan,AnimationSettings pSettings)
		{
			this.mImageSpan=pAnimatedImageSpan;
			this.mSettings=pSettings;
			this.updateHandler=new AnimatedImageUpdateHandler(pTextView);
		}

		@Override
		public void onClick(View widget) {
			
			if(this.mImageSpan.isPlaying())
			{
				this.mImageSpan.stopRendering();
			}else{
				
				this.mImageSpan.playGif(this.mSettings,this.updateHandler);
			}
		}
		
	}
	
}

(3)使用WebView載入Gif

package com.example.gifdemo3;


import android.os.Build;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.webkit.WebView;


public class MainActivity extends Activity {


	private WebView wv_gif;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		wv_gif=(WebView) findViewById(R.id.wv_gif);
		
		if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT)
		{
			
			wv_gif.loadUrl("http://www.wyzu.cn/data/uploadfile/200803/2008032812262650.gif");
		}
	}






}