Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 關於自定義view的思考

關於自定義view的思考

編輯:關於Android編程

對我來說,寫自定義view是一個特麻煩但是寫完之後特有成就感的過程。寫完之後我總是喜歡拿給別人看,去炫耀(當然只是在自己熟悉和關系不錯的人群裡),盡管它們看起來會很簡陋。希望這次寫的東西不會讓大家覺得太過於簡陋,有錯誤的還是得需要各大神的點醒和賜教,有批評建議的也請隨時賜教。

一般的自定義view有三種方式:

繼承具體的view,如textView,button等等; 繼承一個容器view,如linearlayout,relativeLayout等等,然後給該容器添加一些具體的控件來組合成一個新的view; 繼承View。這個方式需要重寫包括onDraw,onMeasure,onLayout,onTouchEvent等等方法來繪制一個新的view

對於這三種方式,我在這裡說一下自己的看法:

首先,對於我來說,我很少使用第一種方式。由於繼承的是一個具體的view,這個view的功能一般都是很強大了。我們能做的,希望去做的,大概是改變形狀或者添加一些形狀功能等等。當然由於我目前基本上沒有做過相應的需求,我也無法去說這個方式到底該怎麼弄。因為在我目前認知裡,如果准備大改某個view,我會用第三種方式,如果需要添加一些模塊或者功能我會使用第二種方式。

然後,對於第二種方式,簡單得說,就是控件組合,把一些控件組合起來,形成一個特殊形狀和功能的view。這個是三種方式中我覺得最方便的,只要做好各個控件的擺放,並提供一些參數設置的方法,或者干脆把這些控件實例提供給使用者,讓使用者自己去操作這些實例。這個方法的缺點也是有的,這裡說的特殊形狀,是由有規則的控件布局而成,所能表達的形狀也是有限的。

對於第三種方式,簡單地說,就是自己畫,利用canvas和paint等一點一線的自己來畫。這種方式是最具創造力的,可以畫出幾乎所有的形狀。(我這裡這麼說,是我感覺這種方式最貼近自定義這個詞)。但同時他的缺點也是巨大的,計算繪制的點、線、框、文字等等位置的值,是這個方式最大的挑戰之一。我經常就發現,在我程序的某段裡,全都是成員變量在那裡加減乘除,如果不給成員變量添加/* ……/這樣的注釋,過不了多久就不知道那段程序到底在干什麼。

對於這三種方式,第一種我幾乎不用,在時間不夠充裕的時候我會用第二種方式,在時間充裕的時候和view非常特殊需要自己繪制的時候我會使用第三種方式。

接下來我把我寫過的一個類似seekBar的自定義view拿出來和大家探討一下。這是一個非常簡單的自定義view,就是我們常見的視頻播放控件下邊顯示視頻進度的一個進度條,不同的是他的左右各有一個textView來顯示時間。

\

第二種方式(組合)創建自定義view

我用第二種方式寫的代碼:

自定義view:

package test2;

import android.content.Context;
import android.util.AttributeSet;
import android.view.Gravity;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;

public class MySeekbarView extends LinearLayout{

    private TextView mTvCurrentTime;
    private TextView mTvTotalTime;
    private SeekBar mSeekbar;

    public MySeekbarView(Context context, AttributeSet attrs) {
        super(context, attrs);
        LinearLayout ll = new LinearLayout(context);
        ll.setGravity(Gravity.CENTER_VERTICAL);
        ll.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
        mTvCurrentTime = new TextView(context);
        mTvCurrentTime.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
        mTvTotalTime = new TextView(context);
        mTvCurrentTime.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
        mSeekbar = new SeekBar(context);
        mSeekbar.setLayoutParams(new LayoutParams(0, LayoutParams.WRAP_CONTENT, 1));
        ll.addView(mTvCurrentTime);
        ll.addView(mSeekbar);
        ll.addView(mTvTotalTime);
        this.addView(ll);
    }

    public TextView getCurrentTimeTextView(){
        return mTvCurrentTime;
    }

    public TextView getTotalTimeTextView(){
        return mTvTotalTime;
    }

    public SeekBar getSeekbar(){
        return mSeekbar;
    }

}

Activity的布局文件:




     

Activity:

package test2;

import com.example.test2.R;

import android.app.Activity;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.widget.SeekBar;
import android.widget.TextView;

public class Test2_seekbar extends Activity{

    private MySeekbarView view;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test2_seekbar_layout);
        view = (MySeekbarView)findViewById(R.id.MySeekbarView);
        TextView tv1 = view.getCurrentTimeTextView();
        tv1.setText("01:00");
        TextView tv2 = view.getTotalTimeTextView();
        tv2.setText("09:00");
        SeekBar seekbar = view.getSeekbar();
        seekbar.setThumb(ContextCompat.getDrawable(this, R.drawable.video_pb_indicator));
        seekbar.setProgress(50);

    }
}

結果:

\

有沒有覺得很方便?在view的數量不多的情況下,直接新建view然後用addView()很方便快捷,但在view的數量有點大或者覺得腦子裡形象不是很直觀的情況下,我們一般會寫一個布局然後通過LayoutInflater的
inflate方法來直接獲得自己想要的這個已經布局好的view。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPtfUtqjS5XZpZXeyvL7WtcSyvL7WzsS8/qO6PC9wPg0KPHByZSBjbGFzcz0="brush:java;">

修改之後的自定義view:

package test2;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;

import com.example.test2.R;

public class MySeekbarView extends LinearLayout{

    private TextView mTvCurrentTime;
    private TextView mTvTotalTime;
    private SeekBar mSeekbar;
    private View view;

    public MySeekbarView(Context context, AttributeSet attrs) {
        super(context, attrs);

        view = LayoutInflater.from(context).inflate(R.layout.myseekbar_view_layout, this);
        mTvCurrentTime = (TextView)view.findViewById(R.id.tv1);
        mTvTotalTime = (TextView)view.findViewById(R.id.tv2);
        mSeekbar = (SeekBar)view.findViewById(R.id.seekbar);
    }

    public TextView getCurrentTimeTextView(){
        return mTvCurrentTime;
    }

    public TextView getTotalTimeTextView(){
        return mTvTotalTime;
    }

    public SeekBar getSeekbar(){
        return mSeekbar;
    }

}

結果是一樣。

有沒有發現,哪裡不一樣。哈哈,發沒發現我都要講了。這是一個關於LayoutInflater的故事。我是在這個地方獲得了很厚重的知識:Android LayoutInflater深度解析 給你帶來全新的認識 。

這裡邊博主告訴我們LayoutInflater的Inflate的三個重構方法的差別:

Inflate(resId , null ):返回temp,沒有執行setLayoutParams和addView

Inflate(resId , root,false ):返回temp,執行setLayoutParams,沒有執行addView

Inflate(resId , root, true ):返回root,執行addView(view,params)

(呃在我那個程序中看到Inflate(resId , root)後沒有addView()方法也可以正常顯示,說明第三個參數默認為true。)

這樣看來在使用Inflate方法的時候有三種選擇:

1.
temp = LayoutInflater.from(context).Inflate(resId , null );
temp.setLayoutParams(…);
this.addView(temp);

2.
temp = LayoutInflater.from(context).Inflate(resId , root,false );
this.addView(temp);

3.
LayoutInflater.from(context).Inflate(resId , root,true );

可想而知他們之間的差別:

3是最簡單的情況,添加單個temp,無特殊params要求;

2其次,可以添加多個temp,無特殊params要求;

1最麻煩,既可以添加多個view又可以添加特殊params要求,唯一不爽的是,需要給每個temp都添加一遍;

這裡我還得提醒一下各位兄弟姐妹們(我最近在自定義的時候犯的錯誤,好不容易才找出來,希望各位兄姐妹們看了以後不要笑,也希望你們不會遇到這樣的問題):

1.一定要盡量把那幾個構造方法的重構都寫上。其實以我目前的經驗,對我有用的重構也就只有public MySeekbarView(Context context)和public MySeekbarView(Context context, AttributeSet attrs)。第一個我覺得主要是在隨時隨地通過context新建view的時候調用,第二個我覺得主要是在利用layout布局來創建view的時候調用。inflate方法中設置的layoutParams是從attrs中得到的,構造函數中沒有傳入AttributeSet attrs或者傳入的為空,inflate中設置的layoutParams為null,然後你就會看見一片空白;

2.盡量利用布局創建view;

3.不通過布局創建view的時候一定不要忘記setLayoutParams,通過布局創建view但沒有設置layoutParams時也要記得添加layoutParams;

4.這些對於linearlayout和relativelayout是有效的,對於viewGroup,呃,我並不是很確定,以後細細研究過後再來探討,不過我認為linearlayout和relativelayout已經包含了很多內容;

第三種方式(draw)創建自定義view

是不是有些亂了?我只能說亂的還在後邊。接下來我們來交流一下用第三種方法來自定義view。

package mydemo.myseekbardemo;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.View;

public class MyView extends View{

    private Context mContext;
    /**
     * view的寬
     */
    private int mViewWidth;
    /**
     * view的高
     */
    private int mViewHeight;
    /**
     * 底部進度條的寬
     */
    private int mBottomBarWidth;
    /**
     * 進度條的高
     */
    private int mBarHeight = 6;
    /**
     * 底部進度條左邊位置
     */
    private int mBottomBarLeft;
    /**
     * 底部進度條上邊位置
     */
    private int mBottomBarTop;
    /**
     * 底部進度條右邊位置
     */
    private int mBottomBarRight;
    /**
     * 底部進度條底邊位置
     */
    private int mBottomBarBottom;

    /**
     * 頂部進度條的右邊位置
     */
    private int mTopBarRight;
    /**
     * 文字的寬
     */
    private int mTvWidth;
    /**
     * 文字的高
     */
    private int mTvHeight;

    /**
     * 圖片對象
     */
    private Bitmap mBitmapOfCirlcle = null;

    /**
     * 圖片可見度,在0~255之間
     */
    private int mBitmapAlpha = 255;
    /**
     * 規定圖片的寬
     */
    private int mBitmapWidth = 30;
    /**
     * 規定圖片的高
     */
    private int mBitmapHeight = 30;
    /**
     * 畫筆對象
     */
    private Paint mPaint;

    /**
     * 判斷是否使用用戶的圖片
     */
    private boolean hasCirclePic = false;

    /**
     * 圓圈圓心的x坐標
     */
    private int mCircleX;
    /**
     * 圓圈圓心的y坐標
     */
    private int mCircleY;

    /**
     * 文字大小
     */
    private float mTextSize = 35;
    /**
     * 一些間距
     */
    private int mPadding = 10;
    /**
     * 圓圈的半徑
     */
    private int mRadius = 16;
    /**
     * 頂部進度條的右邊位置同時也是圓圈的x坐標
     */
    private int mPosition;
    /**
     * 頂部進度條初始百分比進度
     */
    private float mOriginalPercent = 0f;
    /**
     * 文本的顏色
     */
    private int mTextColor = Color.WHITE;
    /**
     * 底部進度條的顏色
     */
    private int mBottomBarColor = Color.GRAY;
    /**
     * 頂部進度條的顏色
     */
    private int mTopBarColor = Color.BLUE;
    /**
     * 圓圈的顏色
     */
    private int mCircleColor = Color.WHITE;

    /**
     * 用戶點擊位置狀態
     */
    private int mWhereClickedState;
    /**
     * 用戶點擊在圓圈上
     */
    private final int WHERR_CLICKED_CIRCLE = 0;
    /**
     * 用戶點擊在進度條上
     */
    private final int WHERR_CLICKED_BAR = 1;
    /**
     * 用戶點擊在其他部分
     */
    private final int WHERR_CLICKED_VIEW = 2;

    /**
     * 實時時間文本
     */
    private String mCurrentTimeString = "00:00";
    /**
     * 總時間文本
     */
    private String mTotalTimeString = "00:00";

    /**
     * 手勢管理對象
     */
    private GestureDetector mGestureDetector;



    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);

        Log.i("tag", "MyView");

        mContext = context;
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);


        mGestureDetector  = new GestureDetector(mContext, new OnGestureListener() {

            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                if(mWhereClickedState == WHERR_CLICKED_CIRCLE){
                }else if(mWhereClickedState == WHERR_CLICKED_BAR){
                    mPosition = (int) Math.round(e.getX()+0.5);
                    if(mPosition >= mBottomBarRight){
                        mPosition = mBottomBarRight;
                    }else if(mPosition <= mBottomBarLeft){
                        mPosition = mBottomBarLeft;
                    }
                    MyView.this.invalidate();
                }
                return false;
            }

            @Override
            public void onShowPress(MotionEvent e) {
                // TODO Auto-generated method stub

            }

            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
                    float distanceY) {
                if(mWhereClickedState == WHERR_CLICKED_CIRCLE){
                    mPosition = (int) Math.round(e2.getX()+0.5);
                    if(mPosition >= mBottomBarRight){
                        mPosition = mBottomBarRight;
                    }else if(mPosition <= mBottomBarLeft){
                        mPosition = mBottomBarLeft;
                    }
                    MyView.this.invalidate();
                }else if(mWhereClickedState == WHERR_CLICKED_BAR){
                }
                return false;
            }

            @Override
            public void onLongPress(MotionEvent e) {
                // TODO Auto-generated method stub

            }

            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
                    float velocityY) {
                // TODO Auto-generated method stub
                return false;
            }

            @Override
            public boolean onDown(MotionEvent e) {
                float event_x = e.getX();
                float event_y = e.getY();
                mWhereClickedState = judgeWhereClicked(event_x, event_y);
                if(mWhereClickedState == WHERR_CLICKED_VIEW){
                    return false;
                }else{
                    return true;
                }
            }
        });
    }

    /**
     * 獲得文本的寬高
     */
    private void initTvWidthAndHeight() {
        Rect rect = new Rect();
        mPaint.getTextBounds("00:00", 0, 5, rect);
        mTvWidth = rect.width();
        mTvHeight = rect.height();
    }

    @Override
    protected void onDraw(Canvas canvas) {

        Log.i("tag", "onDraw");

        mTopBarRight = mPosition;
        mCircleX = mTopBarRight;

        mPaint.setColor(mTextColor);
        canvas.drawText(mCurrentTimeString, mPadding, mBottomBarBottom+Math.round(mTvHeight/4.0+0.5), mPaint);
        canvas.drawText(mTotalTimeString, mViewWidth - mTvWidth - mPadding, mBottomBarBottom+Math.round(mTvHeight/4.0+0.5), mPaint);

        mPaint.setColor(mBottomBarColor);
        canvas.drawRect(mBottomBarLeft, mBottomBarTop, mBottomBarRight, mBottomBarBottom, mPaint);

        mPaint.setColor(mTopBarColor);
        canvas.drawRect(mBottomBarLeft, mBottomBarTop, mTopBarRight, mBottomBarBottom, mPaint);

        if(mBitmapOfCirlcle != null){
            mPaint.setAlpha(mBitmapAlpha);
            canvas.drawBitmap(mBitmapOfCirlcle, mCircleX-mBitmapWidth/2, mCircleY - mBitmapHeight/2, mPaint);
        }else{
            mPaint.setColor(mCircleColor);
            canvas.drawCircle(mCircleX, mCircleY, mRadius, mPaint);
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        mPaint.setTextSize(mTextSize);
        //獲得文本的寬高
        initTvWidthAndHeight();

        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        if(widthMode == MeasureSpec.AT_MOST){
            mViewWidth = mTvWidth*4;
        }else if(widthMode == MeasureSpec.EXACTLY){
            if(mTvWidth*4 >= widthSize){
                mViewWidth = mTvWidth*4;
            }else{
                mViewWidth = widthSize;
            }
        }

        widthMeasureSpec = MeasureSpec.makeMeasureSpec(mViewWidth, MeasureSpec.EXACTLY);

        if(heightMode == MeasureSpec.AT_MOST){
            mViewHeight = mTvHeight + 4*mPadding;
        }else if(heightMode == MeasureSpec.EXACTLY){
            if(heightSize <= mTvHeight+4*mPadding ){
                mViewHeight = mTvHeight+4*mPadding;
            }else{
                mViewHeight = heightSize;
            }
        }

        heightMeasureSpec = MeasureSpec.makeMeasureSpec(mViewHeight, MeasureSpec.EXACTLY);

        mBottomBarWidth = mViewWidth - 2*mTvWidth - 6*mPadding;

        mBottomBarLeft = mTvWidth + 3*mPadding;
        mBottomBarRight = mViewWidth - mTvWidth - 3*mPadding;
        mBottomBarBottom = (mViewHeight - mTvHeight)/2+mTvHeight;
        mBottomBarTop = mBottomBarBottom-mBarHeight;

        mCircleY = mBottomBarBottom - mBarHeight/2;

        mPosition = (int) Math.round(mBottomBarWidth * mOriginalPercent+0.5)+mBottomBarLeft;
        if(mPosition <= mBottomBarLeft){
            mPosition = mBottomBarLeft;
        }else if(mPosition >= mBottomBarRight){
            mPosition = mBottomBarRight;
        }

        if(mBitmapOfCirlcle != null){
            mBitmapOfCirlcle = Bitmap.createScaledBitmap(mBitmapOfCirlcle, mBitmapWidth, mBitmapHeight, false);
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    /**
     * 設置總時間,默認格式"--:--"
     * @param strOfTime
     */
    public void setTotalTimeString(String strOfTime){

        Log.i("tag", "setTotalTimeString");

        mTotalTimeString = strOfTime;
    }

    /**
     * 設置實時時間,默認格式"--:--"
     * @param strOfTime
     */
    public void setCurrentTimeString(String strOfTime){

        Log.i("tag", "setCurrentTimeString");

        mCurrentTimeString = strOfTime;
    }


    /**
     * 設置圖片的透明度
     * @param alpha 0~255
     */
    public void setBitmapAlpha(int alpha){

        Log.i("tag", "setBitmapAlpha");

        if(alpha < 0){
            alpha = 0;
        }else if(alpha > 255){
            alpha = 255;
        }
        mBitmapAlpha = alpha;
    }
    /**
     * 獲得現在所在位置的百分比進度值
     * @return float
     */
    public float getPercent(){
        float percent = (mBottomBarRight - mPosition)*1.0f/mBottomBarWidth;
        if(percent < 0.0){
            percent = 0f;
        }else if(percent > 1){
            percent = 1f;
        }
        return percent;
    }
    /**
     * 獲得當前進度值
     * @return int
     */
    public int getProgress(){

        float percent = (mBottomBarRight - mPosition)*1.0f/mBottomBarWidth;
        if(percent < 0.0){
            percent = 0f;
        }else if(percent > 1){
            percent = 1f;
        }

        int progress = (int) Math.round(percent*100+0.5);
        if(progress < 0){
            progress = 0;
        }else if(progress >100){
            progress = 100;
        }

        return progress;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(mGestureDetector != null){
            return  mGestureDetector.onTouchEvent(event);
        }else{
            return super.onTouchEvent(event);
        }
    }
    /**
     * 判斷用戶點擊位置狀態
     * @param x 用戶點擊的x坐標
     * @param y 用戶點擊的y坐標
     * @return 返回用戶點擊未知狀態:
     *          WHERR_CLICKED_CIRCLE
     *          WHERR_CLICKED_BAR
     *          WHERR_CLICKED_VIEW
     */
    public int judgeWhereClicked(float x, float y){
        //s_x,s_y用來界定圓圈或者代替圓圈的圖片的區域,s_x表示x坐標方向,圓心到區域邊界的距離
        int s_x = 0;
        int s_y = 0;
        if(hasCirclePic){
            s_x = mBitmapWidth/2;
            s_y = mBitmapHeight/2;
        }else{
            s_x = mRadius;
            s_y = mRadius;
        }

        if( (x>=(mCircleX-s_x) && x<=(mCircleX + s_x)) && (y>=(mCircleY-s_y) && y<=(mCircleY + s_y)) ){
            //s_x和s_y界定的區域,即圓圈區域
            return WHERR_CLICKED_CIRCLE;
        }else if((x>=mBottomBarLeft && x<=mBottomBarRight) || (y <= mBottomBarBottom+5 && y >= mBottomBarTop+5 )){
            //進度條上除了圓圈區域的其他可響應點擊部分
            return WHERR_CLICKED_BAR;
        }else{
            //view除了上述兩部分的部分
            return WHERR_CLICKED_VIEW;
        }
    }


    /**
     * 設置初始百分比進度,0.0~1.0f
     * @param percent
     */
    public void setOriginalPercent(float percent){
        mOriginalPercent = percent;
        if(mOriginalPercent<0){
            mOriginalPercent = 0.0f;
        }else if(mOriginalPercent > 1.0f){
            mOriginalPercent = 1.0f;
        }
    }
    /**
     * 設置初始進度值,默認進度最大值為100
     * @param progress
     */
    public void setOriginalProgress(int progress){

        mOriginalPercent = progress/100f;
        if(mOriginalPercent<0){
            mOriginalPercent = 0.0f;
        }else if(mOriginalPercent > 1.0f){
            mOriginalPercent = 1.0f;
        }
    }

    /**
     * 通過傳入進度百分比來更新進度條
     * @param percent
     */
    public void updatePositionFromPercent(float percent){

        Log.i("tag", "updatePositionFromPercent");
        if(percent<0){
            percent = 0.0f;
        }else if(percent > 1.0f){
            percent = 1.0f;
        }

        mPosition = (int) Math.round(mBottomBarWidth * percent+0.5)+mBottomBarLeft;
        if(mPosition <= mBottomBarLeft){
            mPosition = mBottomBarLeft;
        }else if(mPosition >= mBottomBarRight){
            mPosition = mBottomBarRight;
        }

        this.invalidate();
    }

    /**
     * 通過傳入進度值來更新進度條
     * @param percent
     */
    public void updatePositionFromProgress(int progress){

        float percent = progress/100f;
        if(percent<0){
            percent = 0.0f;
        }else if(percent > 1.0f){
            percent = 1.0f;
        }

        mPosition = (int) Math.round(mBottomBarWidth * percent+0.5)+mBottomBarLeft;
        if(mPosition <= mBottomBarLeft){
            mPosition = mBottomBarLeft;
        }else if(mPosition >= mBottomBarRight){
            mPosition = mBottomBarRight;
        }

        this.invalidate();
    }

    /**
     * 設置文字顏色
     * @param color
     */
    public void setTextColor(int color){
        mTextColor = color;
    }
    /**
     * 設置文字大小
     * @param size
     */
    public void setTextSize(int size){
        mTextSize = size;
    }

    /**
     * 設置底部進度條顏色
     * @param color
     */
    public void setBottomBarColor(int color){
        mBottomBarColor = color;
    }

    /**
     * 設置頂部進度條顏色
     * @param color
     */
    public void setTopBarColor(int color){
        mTopBarColor = color;
    }

    /**
     * 獲得實時時間文本
     * @return
     */
    public String getCurrentTimeString(){
        return mCurrentTimeString;
    }

    /**
     * 獲得總時間文本
     * @return
     */
    public String getTotalTimeString(){
        return mTotalTimeString;
    }
    /**
     * 用其他圖片更換進度條上的圓圈
     * @param bitmap
     */
    public void setThumbBitmap(Bitmap bitmap){
        mBitmapOfCirlcle = bitmap;
    }
}

哈哈,是不是感覺有點頭大。其實很簡單,一共兩個部分,一是計算位置,二是繪制圖形。其中最看重的就是位置的計算,位置計算好了,繪制就是分分鐘的事了。canvas的繪制功能很強大,一切形狀功能都可以輕松繪制出來。看著圖形從一個點一條線到一個復雜圖形甚至是擁有動畫屬性,這個過程是很有成就感的,也是我最喜歡的過程。

對canvas和paint了解還不深的我就不在這裡露短了,只想告訴大家一些我知道的需要注意的問題:

1.現在的手機基本上都是支持硬件加速的,而並不是所有的canvas的操作都支持硬件加速的,比如canvas.drawPicture()等,所以在使用自定義view特別是自己繪制的自定義view的時候需要注意是否應該開啟硬件加速;

2.最好不要頻繁得創建paint對象,頻繁創建對象本就是編程的大忌,繪制的過程更是如此;

3.想要使得view擁有動畫屬性,只要在靜止的位置上添加偏移量,改變偏移量,就能完成動畫的繪制;

4.盡量給成員變量添加/** ……/這樣的注釋,特別是在計算位置特別復雜的自定義view裡,這樣會更加便於閱讀和回憶;

第一次寫博客,沒想到會花這麼長的時間。以前覺得博客內容很少,寫的應該蠻快的。今天我真真切切的感受到,寫一篇博客真是相當不容易的。也許只是像我這種小新才會有這種感覺把。不過還蠻有成就感的,盡管我覺得真的沒有寫些什麼。最後還是希望有什麼批評建議的請隨時賜教!

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved