Android自定義View之如期相遇的百分比進度條RatioProgress

需求

簡述:

當進入比賽詳情頁面時,根據點贊數按比例分割整個屏幕寬度,這個過程以動態進度條的形式顯示

實際應用效果圖:

這裏寫圖片描述

Demo效果圖:

這裏寫圖片描述

分析

自定義View的基本步驟:

  • 自定義View的屬性
  • 在View的構造方法中獲得我們自定義的屬性
  • 重寫onMesure(非必須,大部分情況下需要)
  • 重寫onDraw

自定義View屬性:

在res/values/ 下建立一個attrs.xml ,在裏面定義我們的屬性和聲明我們的整個樣式,format是指該屬性的取值類型

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="RatioProgress">
        <attr name="direction" format="string" />
        <attr name="progressColor" format="color" />
    </declare-styleable>

</resources>

這裏,我根據需求定義了兩個屬性,分別爲direction和progressColor

  • direction表示進度條的繪製方向,有兩個值,分別爲“left”和“right”

“left”表示從左到右進行顯示,“right”表示從右向左進行顯示

  • progressColor表示進度條的顯示背景顏色

RatioProgress分析:

  • 通過rectBgBounds 繪製背景矩形,進行佔位,背景設置爲透明的
  • 通過rectProgressBounds來繪製進度條,背景顏色就是通過如下自定義屬性進行設置

    sus:progressColor="@color/CommonTextSelect"
  • bgPaint和progressPaint分別爲繪製背景和進度條的畫筆

關鍵步驟之重寫onDraw方法

    @Override
    protected void onDraw(Canvas canvas) {

        super.onDraw(canvas);
        canvas.drawRect(rectBgBounds, bgPaint);

        if (TextUtils.equals(direction, "left")) {
            rectProgressBounds = new RectF(0, 0, progress, layout_height);
        } else if (TextUtils.equals(direction, "right")) {
            rectProgressBounds = new RectF(getWidth() - progress, 0, getWidth(), layout_height);
        }else{
            rectProgressBounds = new RectF(0, 0, progress, layout_height);
        }
        canvas.drawRect(rectProgressBounds, progressPaint);

    }
  • 這裏根據direction屬性來設置rectProgressBounds 的座標位置

  • 我在 setupBounds()中通過start方法開啓一個線程

    final Runnable r = new Runnable() {
        public void run() {
            running = true;
            Log.e("thread", "progress="+progress);
            Log.e("thread", "getWidth()="+getWidth());
            while (progress < getWidth()) {
                incrementProgress();
                //progress++;
                try {
                    Thread.sleep(sleepDelay);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            running = false;
        }
    };

    public void start(){
        if (!running) {
            progress = 0;
            Thread s = new Thread(r);
            s.start();
        }
    }
  • 並通過incrementProgress方法遞增progress,然後再通過handler發消息不斷進行繪製
   /**
     * Increment the progress by 1 (of 100)
     */
    public void incrementProgress() {
        isProgress = true;
        progress++;
        /*
         * if (progress > 200) progress = 100;
         */
        spinHandler.sendEmptyMessage(0);
    }

RatioProgress 完整代碼:

public class RatioProgress extends View {

    // Sizes (with defaults)
    private int layout_height = 0;

    private int layout_width = 0;

    // Colors (with defaults)
    private int bgColor = Color.TRANSPARENT;

    //private int progressColor = 0xFF339933;

    // Paints
    private Paint progressPaint = new Paint();

    private Paint bgPaint = new Paint();

    // Rectangles
    private RectF rectBgBounds = new RectF();

    private RectF rectProgressBounds = new RectF();

    int progress = 0;

    boolean isProgress;

    private String direction;

    /**
     * progress的顏色
     */
    private int progressColor;

    boolean running;

    int sleepDelay;


    public int getSleepDelay() {
        return sleepDelay;
    }

    public void setSleepDelay(int sleepDelay) {
        this.sleepDelay = sleepDelay;
    }

    private Handler spinHandler = new Handler() {
        /**
         * This is the code that will increment the progress variable and so
         * spin the wheel
         */
        @Override
        public void handleMessage(Message msg) {
            invalidate();
        }
    };

    /**
     * @param context
     */
    public RatioProgress(Context context) {
        this(context, null);
    }

    /**
     * @param context
     * @param attrs
     */
    public RatioProgress(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    /**
     * @param context
     * @param attrs
     * @param defStyleAttr
     */
    public RatioProgress(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        /**
         * 獲得我們所定義的自定義樣式屬性
         */
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.RatioProgress, defStyleAttr, 0);
        int n = a.getIndexCount();
        for (int i = 0; i < n; i++)
        {
            int attr = a.getIndex(i);
            switch (attr)
            {
            case R.styleable.RatioProgress_direction:
                direction = a.getString(attr);
                Log.e("direction-----------------", direction);
                break;
            case R.styleable.RatioProgress_progressColor:
                progressColor = a.getColor(attr, Color.TRANSPARENT);
                break;
            }

        }
        a.recycle();

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        // Share the dimensions
        layout_width = w;
        Log.i("layout_width", layout_width + "");

        layout_height = h;
        Log.i("layout_height", layout_height + "");
        setupBounds();
        setupPaints();
        invalidate();

    }

    private void setupPaints() {
        bgPaint.setColor(bgColor);
        bgPaint.setAntiAlias(true);
        bgPaint.setStyle(Style.FILL);

        progressPaint.setColor(progressColor);
        progressPaint.setAntiAlias(true);
        progressPaint.setStyle(Style.FILL);

    }

    private void setupBounds() {
        int width = getWidth(); // this.getLayoutParams().width;
        Log.i("width", width + "");
        int height = getHeight(); // this.getLayoutParams().height;
        Log.i("height", height + "");
        rectBgBounds = new RectF(0, 0, width, height);
        start();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.drawRect(rectBgBounds, bgPaint);

        Log.i("progress", progress + "");
        if (TextUtils.equals(direction, "left")) {
            rectProgressBounds = new RectF(0, 0, progress, layout_height);
        } else if (TextUtils.equals(direction, "right")) {
            rectProgressBounds = new RectF(getWidth() - progress, 0, getWidth(), layout_height);
        }else{
            rectProgressBounds = new RectF(0, 0, progress, layout_height);
        }
        canvas.drawRect(rectProgressBounds, progressPaint);

    }

    /**
     * Increment the progress by 1 (of 100)
     */
    public void incrementProgress() {
        isProgress = true;
        progress++;
        /*
         * if (progress > 200) progress = 100;
         */
        spinHandler.sendEmptyMessage(0);
    }

    /**
     * Increment the progress by 1 (of 100)
     */
    public void unIncrementProgress() {
        isProgress = true;
        progress--;
        /*
         * if (progress < 1) progress = 100;
         */
        spinHandler.sendEmptyMessage(0);
    }

    /**
     * Set the progress to a specific value
     */
    public void setProgress(int i) {

        progress = i;
        spinHandler.sendEmptyMessage(0);
    }

    final Runnable r = new Runnable() {
        public void run() {
            running = true;
            Log.e("thread", "progress="+progress);
            Log.e("thread", "getWidth()="+getWidth());
            while (progress < getWidth()) {
                incrementProgress();
                //progress++;
                try {
                    Thread.sleep(sleepDelay);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            running = false;
        }
    };

    public void start(){
        if (!running) {
            progress = 0;
            Thread s = new Thread(r);
            s.start();
        }
    }
}

佈局以及代碼中的使用:

佈局文件

這裏在LinearLayout 中定義了兩個RatioProgress

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:sus="http://schemas.android.com/apk/res/com.soulrelay.ratioprogress"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <com.soulrelay.ratioprogress.RatioProgress
        android:id="@+id/left_ratio_progress"
        android:layout_width="match_parent"
        android:layout_height="4dp"
        android:layout_marginTop="100dp"
        sus:direction="left"
        sus:progressColor="@color/CommonTextSelect" />

    <com.soulrelay.ratioprogress.RatioProgress
        android:id="@+id/right_ratio_progress"
        android:layout_width="match_parent"
        android:layout_height="4dp"
        android:layout_marginLeft="4dp"
        android:layout_marginTop="100dp"
        sus:direction="right" 
        sus:progressColor="@color/CommonSelect"/>

</LinearLayout>

實際java代碼中的控制

這裏主要是設置leftRatioProgress和rightRatioProgress的寬度,以及通過設置View中的線程休眠時間來控制進度條可以同時相遇

public class MainActivity extends Activity {

    RatioProgress leftRatioProgress;
    RatioProgress rightRatioProgress;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        WindowManager manager = ((WindowManager) this
                .getSystemService(Context.WINDOW_SERVICE));

        DisplayMetrics dm = new DisplayMetrics();
        manager.getDefaultDisplay().getMetrics(dm);
        final int w = dm.widthPixels;

        leftRatioProgress = (RatioProgress) findViewById(R.id.left_ratio_progress);
        LayoutParams lp = leftRatioProgress.getLayoutParams();
        lp.width = w/3;
        leftRatioProgress.setLayoutParams(lp);

        rightRatioProgress = (RatioProgress) findViewById(R.id.right_ratio_progress);
        LayoutParams lp1 = rightRatioProgress.getLayoutParams();
        lp1.width = w*2/3;
        rightRatioProgress.setLayoutParams(lp1);

        leftRatioProgress.setSleepDelay(6);
        rightRatioProgress.setSleepDelay(3);

    }
}

實際代碼中我是根據用戶的點贊數來分割屏幕寬度,設置View中的休眠時間

以下代碼僅供參考:

   /**
     * 進度條形式顯示贊數的比例
     *
     * @param matchInfo
     * @author sushuai
     */
    private void initRatioProgress(MatchInfo matchInfo) {
        int width = SystemUtil.getScreenDisplayMinWidth(context);
        int leftWeight = matchInfo.getTeam1().getLikes();
        int rightWeight = matchInfo.getTeam2().getLikes();
        int leftWidth = 0, rightWidth = 0;
        if (leftWeight == 0 && rightWeight == 0) {
            leftWidth = rightWidth = width / 2;
        } else if (leftWeight == 0) {
            rightWidth = width;
        } else if (rightWeight == 0) {
            leftWidth = width;
        } else {
            leftWidth = width * leftWeight / (leftWeight + rightWeight);
            rightWidth = width * rightWeight / (leftWeight + rightWeight);
        }

        if (leftRatioProgress != null) {
            LayoutParams lp = leftRatioProgress.getLayoutParams();
            lp.width = leftWidth;
            leftRatioProgress.setLayoutParams(lp);
            if (leftWidth >= rightWidth) {
                leftRatioProgress.setSleepDelay(1);
            } else if (leftWidth != 0) {
                leftRatioProgress.setSleepDelay(rightWidth / leftWidth);
            }

        }

        if (rightRatioProgress != null) {
            LayoutParams lp = rightRatioProgress.getLayoutParams();
            lp.width = rightWidth;
            rightRatioProgress.setLayoutParams(lp);
            if (leftWidth >= rightWidth && rightWidth != 0) {
                rightRatioProgress.setSleepDelay(leftWidth / rightWidth);
            } else {
                rightRatioProgress.setSleepDelay(1);

            }
        }
    }

其它

Demo下載:

傳送門

參考鏈接:

1、http://blog.csdn.net/wangjinyu501/article/details/38298737
1、http://blog.csdn.net/lmj623565791/article/details/24252901/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章