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");
}
}
}