Android自定义进度条

转载请注明原创出处,谢谢!
效果图

ps:最近做一个字体放大的需求,有一个进度条的效果使用到了自定义view

需求:

  • 可滑动
  • 进度为0时,左边显示0,右边显示“大”
  • 进度为5时,左边显示“小”,右边显示5
  • 进度为1-4时,左边显示“小”,右边显示“大”,中间进度位置显示具体数额
  • 触摸时,显示距离触摸位置最近的进度

实现思路:

  • 整体使用FrameLayout,并设置背景色
  • 里面放左右2个TextView(任意view,也可以是图片),用来显示”小“和”大“
  • 自定义ViewGroup(即MySeekBar),处理触摸事件,控制MySeekBar的第一子View(TextView)位置达到效果
  • MySeekBar只处理位置,具体显示内容,通过监听事件交给页面自己处理,例如下面Activity实例代码
package com.example.myapplication;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;

/**
 * 自定义字体放大进度条ViewGroup
 *
 * @author ricco
 */
public class FontSizeSeekBar extends ViewGroup {

    /**
     * 当前进度
     */
    private int mCurProgress = 0;

    /**
     * 最大进度
     */
    private int mMaxProgress = 5;

    /**
     * 每个进度点的位置
     */
    private float[] touchPoint = new float[mMaxProgress + 1];

    /**
     * 2个进度点中心的的距离
     */
    private float step = 0;
    private int w, h;

    public FontSizeSeekBar(Context context) {
        super(context);
    }

    public FontSizeSeekBar(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        w = MeasureSpec.getSize(widthMeasureSpec);
        h = MeasureSpec.getSize(heightMeasureSpec);
        for (int i = 0; i < getChildCount(); i++) {
            measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
        }
        init();
    }

    private void init() {
        // 2个圆心的位置
        step = 1.0f * (w - h) / mMaxProgress;
        touchPoint = new float[mMaxProgress + 1];
        for (int i = 0; i < touchPoint.length; i++) {
            touchPoint[i] = h / 2.f + step * i;
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        layoutProgressByCur();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // 根据触摸x的指标位置,计算进度
        int progress = calculateProgressByEventX(touchPoint, event.getX());
        if (mCurProgress != progress) {
            mCurProgress = progress;
            // 重新摆明
            layoutProgressByCur();
        }
        return true;
    }

    /**
     * 根据当前进度更新显示
     */
    public void layoutProgressByCur() {
        View child = getChildAt(0);
        if (child != null) {
            if (mCurProgress <= 0) {
                // 小于等于0的,全部按照0处理
                child.layout(0, 0, h, h);
            } else if (mCurProgress >= mMaxProgress) {
                // 大于等于最大进度的,全部按照最大进度处理
                child.layout(w - h, 0, w, h);
            } else {
                child.layout((int) (step * mCurProgress), 0, (int) (step * mCurProgress) + h, h);
            }
            if (mListener != null) {
                mListener.onProgressChanged(child, mCurProgress);
            }
        }
    }

    /**
     * 设置最前值
     * <p>
     * setMaxProgress或setCurProgress后,最好手动调用layoutProgressByCur
     */
    public void setCurProgress(int curProgress) {
        if (curProgress < 0) {
            curProgress = 0;
        }
        this.mCurProgress = Math.min(curProgress, mMaxProgress);
    }

    public int getCurProgress() {
        return Math.min(mCurProgress, mMaxProgress);
    }

    /**
     * 设置最大值
     * <p>
     * setMaxProgress或setCurProgress后,最好手动调用layoutProgressByCur
     */
    public void setMaxProgress(int maxProgress) {
        if (mCurProgress > maxProgress) {
            // 必须先调小当前进度,再调最大进度
            return;
        }
        this.mMaxProgress = maxProgress;
        // mCurProgress会印象当前展示样式,需要重新计算位置
        init();
    }

    // Util=========================================================================================

    /**
     * 根据触摸x的指标位置,计算进度
     * <p>
     * 获取一个数的最近的位置
     *
     * @param arr 数组
     * @param num 数
     * @return 最近的位置
     */
    private int calculateProgressByEventX(float[] arr, float num) {
        if (arr != null && arr.length > 0) {
            float[] newArr = new float[arr.length];
            // 最小的绝对值,默认第一最小
            float newArrMin = Math.abs(arr[0] - num);
            for (int i = 0; i < arr.length; i++) {
                // 差的绝对值
                newArr[i] = Math.abs(arr[i] - num);
                newArrMin = Math.min(newArr[i], newArrMin);
            }
            // 最小绝对值的位置,就是最近的点
            for (int i = 0; i < newArr.length; i++) {
                if (newArr[i] == newArrMin) {
                    return i;
                }
            }
        }
        return 0;
    }

    // Listener=====================================================================================

    /**
     * 滑动监听事件
     */
    private OnFontSizeSeekBarChangeListener mListener;

    public void setOnSeekBarChangeListener(OnFontSizeSeekBarChangeListener onListener) {
        this.mListener = onListener;
    }

    public interface OnFontSizeSeekBarChangeListener {
        /**
         * 进度发生变化的回调接口
         *
         * @param childView   滑动的子view
         * @param curProgress 当前进度
         */
        void onProgressChanged(View childView, int curProgress);
    }
}

示例代码

MainActivity

public class MainActivity extends AppCompatActivity
        implements MySeekBar.OnFontSizeSeekBarChangeListener{
    private FontSizeSeekBar mySeekBar;

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

        mySeekBar = findViewById(R.id.mySeekBar);
        mySeekBar.setListener(this);

        mySeekBar.setMaxProgress(5);
        mySeekBar.setCurProgress(0);
        // layoutProgressByCur 会重新摆放view,并且回调监听
        mySeekBar.layoutProgressByCur();
    }

    @Override
    public void onListener(View childView, int curProgress) {
        if (childView instanceof TextView) {
            TextView textView = (TextView) childView;
            String scaleStr = progressToScaleStr(curProgress);
            textView.setText(scaleStr);
            updateUi(Double.parseDouble(scaleStr));
        }
    }

    /**
     * 进度转显示字符串
     */
    private String progressToScaleStr(int progress) {
        double scale = 1 + progress / 10.0;
        DecimalFormat decimalFormat = new DecimalFormat("0.0");
        decimalFormat.setRoundingMode(RoundingMode.UP);
        return decimalFormat.format(scale);
    }
}

MainActivity布局

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

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="32dp"
        android:layout_margin="20dp"
        android:background="@drawable/bg_f4f5f7">

        <TextView
            android:layout_width="32dp"
            android:layout_height="32dp"
            android:layout_gravity="start"
            android:gravity="center"
            android:text="小"
            android:textSize="14sp" />

        <TextView
            android:layout_width="32dp"
            android:layout_height="32dp"
            android:layout_gravity="end"
            android:gravity="center"
            android:text="大"
            android:textSize="18sp" />

        <com.example.myapplication.FontSizeSeekBar
            android:id="@+id/mySeekBar"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:layout_width="32dp"
                android:layout_height="match_parent"
                android:layout_gravity="center"
                android:background="@drawable/bg_737780"
                android:gravity="center"
                android:text="1"
                android:textColor="#FFFFFF"
                android:textSize="14sp" />
        </com.example.myapplication.FontSizeSeekBar>
    </FrameLayout>
</LinearLayout>

选中样式

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <solid android:color="#737780" />

    <corners android:radius="1000dp" />
</shape>

背景样式

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <solid android:color="#f4f5f7" />

    <corners android:radius="1000dp" />
</shape>
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容