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);
- }
- }
- }
- }
完整项目链接:http://download.csdn.net/detail/aehaojiu/8438351
(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");
- }
- }
- }