ビューの進捗バーのカスタマイズ

9755 ワード

清明节の休みを利用してよくカスタムビューを勉强して、少し心得があって、今日记录してみます.
仕事の中で常にいくつかのView効果を実現したいと思っていますが、既成のものがあるかどうかは、長い間定義する必要があります.カスタムビューには、次の3つのアプリケーションがあります.
既存のコントロールでは、個人的な処理(ImageViewなどから継承)を行います.
既存のコントロールは私たちのニーズに満足していません.自分で創造(Viewから継承)する必要があります.いくつかのコントロールを組み合わせて、新しい(ViewGroupから継承された)を生成します.
ここでは2つ目の状況を記録します.
まず、クラスCircleImageProgressBarをカスタマイズし、Viewから継承しながら構築方法を実装します.
public CircleImageProgressBar(Context context) {
        this(context, null);//           
    }

    public CircleImageProgressBar(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);//           
    }

    public CircleImageProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);//           
    }

    public CircleImageProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

ここでは便宜上、他の3つの構築方法はthisキーワードを使用して4つのパラメータの構築方法を呼び出します.res/values/の下にattrsを構築する必要がありますxmlでは、Viewのプロパティをカスタマイズし、スタイル全体を宣言します.


    
        
        
        
        
        

    

ここで定義すると、レイアウトファイルでこれらのパラメータを使用できます.次に、カスタムViewの構築方法を初期化します.
public class CircleImageProgressBar extends View {
    /**    */
    private Paint circlePaint;
    /**      */
    private float circleColor;
    /**      */
    private float radius;
    /**      */
    private float strockWidth;
    /**      ,         */
    private float progressColor;
    /**       */
    private Paint progressPaint;
    /**    */
    private RectF rectF;
    /**      */
    private float currentProgress;

    //    
    private Paint textPaint;
    //      ,    context
    private Context context;

    //      ,            
    .......

    public CircleImageProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        this.context=context;
        //     
        initAttrs(context, attrs);
        //        ,  Android         (onLayout)   (onDraw)       ,            ,    。            
        initVariables();
    }

    // attrs.xml       ,          View      AttributeSet      。
    //      context.obtainStyledAttributes()      TypedArray  。   TypedArray       View     。
    private void initAttrs(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleImageProgressBar, 0, 0);
        radius = typedArray.getDimension(R.styleable.CircleImageProgressBar_radius, 200);//    ,    100
        circleColor = typedArray.getColor(R.styleable.CircleImageProgressBar_circleColor, Color.RED);//       ,     
        strockWidth = typedArray.getDimension(R.styleable.CircleImageProgressBar_strokeWidth, 20);//       ,   20
        progressColor=typedArray.getColor(R.styleable.CircleImageProgressBar_progressColor,Color.RED);
        typedArray.recycle();//TypedArray        ,              recycle()     。
    }

    private void initVariables() {
        circlePaint = new Paint();
        circlePaint.setAntiAlias(true);//      
        circlePaint.setColor((int) circleColor);//     
        circlePaint.setStyle(Paint.Style.STROKE);//    ,   ,     
        circlePaint.setStrokeWidth(strockWidth);//        

        rectF=new RectF();

        progressPaint=new Paint();
        progressPaint.setAntiAlias(true);
        progressPaint.setColor((int) progressColor);
        progressPaint.setStyle(Paint.Style.STROKE);
        progressPaint.setStrokeWidth(strockWidth);

        textPaint=new Paint();
        textPaint.setAntiAlias(true);
        textPaint.setColor((int) progressColor);
        textPaint.setStyle(Paint.Style.STROKE);
        textPaint.setTextSize(DensityUtils.sp2px(context, 22));
    }
}

これらの初期化が完了すると、onDraw()メソッドを書くことができます.これは、カスタムViewの非常に重要な方法であり、カスタムViewの描画はここで完了します.
protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int minimun=Math.min(getWidth()/2,getHeight()/2);//             
        radius=radius>=minimun?minimun:radius;//    
        //    ,      x,   y,  ,  
        canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius-strockWidth/2, circlePaint);

        //          ,  getWidth   。
        rectF.top=getHeight()/2-radius+strockWidth/2;
        rectF.left=getWidth()/2-radius+strockWidth/2;
        rectF.right=getWidth()/2+radius-strockWidth/2;
        rectF.bottom=getHeight()/2+radius-strockWidth/2;

        //       
        int totalProgress=100;
        //              
        //RectF oval:         ,             ,             ,         。
        //float startAngle:     ,0       3    ,       -90 。
        //float sweepAngle:       
        //boolean useCenter:true      ,false      
        //Paint paint:     。
        canvas.drawArc(rectF,-90,currentProgress/totalProgress*360,false,progressPaint);
        //         ,     
        String text=currentProgress+"%";
        float textWidth=textPaint.measureText(text,0,text.length());
        Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
        float textHeight=Math.abs(fontMetrics.ascent)-fontMetrics.descent;
        canvas.drawText(currentProgress+"%",(getWidth()-textWidth)/2,(getHeight()+textHeight)/2,textPaint);
    }

onDraw()メソッドで必要なビューを基本的に描きましたが、明らかに生き生きしていません.進捗は死んでいます.次に、viewのpostInvalidate()メソッドで進捗を更新します.
viewで:
public void updateProgress(int currentProgress){
    this.currentProgress=currentProgress;
    postInvalidate();
}

次にactivityのonResume()メソッドに次のように書きます.
@Override
    protected void onResume() {
        super.onResume();
        new Thread(new Runnable() {
            @Override
            public void run() {
                int progress=0;
                while (progress<=100){
                    circleImageProgressBar.updateProgress(progress);
                    try{
                        Thread.sleep(100);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

次に、カスタムviewのonMeasure()メソッドを見てみましょう.
カスタムViewの測定コアはシステムが提供するMeasureSpecであり、MeasureSpecのコアはmodeとsizeの2つの属性である.
MeasureSpeクラスは測定モードとサイズを1つの32ビットのint型の数値に組み合わせ,そのうち高2ビット表現モード,低30ビット表現サイズで計算にビット演算を用いるのは効率の向上と最適化のためである.
まずmodeを見て
  • UNSPECIFIED:どれだけ大きいか、一般的にはこのモードに関心がありません.
  • EXACTLY:コントロールのlayout_widthプロパティまたはlayout_heightプロパティが特定の数値に指定されている場合、android:layout_width="100 dp"またはmatch_として指定parent属性の場合、システムはEXACTLYモードを使用します.
  • AT_MOST:最大値モード、コントロールのlayout_widthプロパティまたはlayout_heightプロパティwarp_として指定contentの場合、コントロールのサイズは一般的にコントロールのサブコントロールまたはコンテンツの変化に伴って変化します.この場合、コントロールのサイズは親コントロールが許容する最大サイズを超えなければなりません.

  • 次に、onMeasure()メソッドを書き直します.
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)  
    {  
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
        int width;  
        int height ;  
        if (widthMode == MeasureSpec.EXACTLY)  
        {  
            width = widthSize;  
        } else  
        {  
            mPaint.setTextSize(mTitleTextSize);  
            mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds);  
            float textWidth = mBounds.width();  
            int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());  
            width = desired;  
        }  
      
        if (heightMode == MeasureSpec.EXACTLY)  
        {  
            height = heightSize;  
        } else  
        {  
            mPaint.setTextSize(mTitleTextSize);  
            mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds);  
            float textHeight = mBounds.height();  
            int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());  
            height = desired;  
        }  
        setMeasuredDimension(width, height);  
    }  

    これで私たちのカスタムviewは基本的に書き終わり、より良い体験のために、私たちがクリックしたときに、進捗を0にリセットしたいと思っています.ここでは、performClick()とonTouchEvent()の2つの方法が実現できます.この2つの方法はいずれもリスニングを実現することができるが、このViewのonTouchEventタッチリスニングを実現すると、タッチリスニングの戻り値が何であるかにかかわらず、performClick()は実行できなくなり、1つの解決策はonTouchEvent()でperformClick()メソッドを呼び出すことである.
     @Override
        public boolean performClick() {
            currentProgress=0;
            postInvalidate();
            return super.performClick();
        }
    
        //        performClick()   ,              persormClick()  
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (event.getAction()==MotionEvent.ACTION_DOWN){
                Toast.makeText(context, "  ", Toast.LENGTH_SHORT).show();
                return  true;
            }else if (event.getAction()==MotionEvent.ACTION_MOVE){
                Toast.makeText(context, "  ", Toast.LENGTH_SHORT).show();
                return true;
            }else if (event.getAction()==MotionEvent.ACTION_UP){
                Toast.makeText(context, "  ", Toast.LENGTH_SHORT).show();
                performClick();
                return true;
            }
            return super.onTouchEvent(event);
        }
    オーラ、カスタムviewの記録についてこれだけ書きましょう.皆さん、何かいいアドバイスがありますか.ありがとうございます.