【问题标题】:How to constraint a view (TextView) to a progressBar current progress?如何将视图(TextView)约束到进度条当前进度?
【发布时间】:2020-01-13 04:38:24
【问题描述】:

我正在处理一个项目,在这个项目中,我使用 progressBars 作为条形图,

即:我得到一个项目列表,然后我检查哪个项目具有最大值并将该最大值设置为所有进度条(项目)的最大进度,

然后我想显示每个项目的值(进度),但是我想将它附加到特定项目的进度,换句话说我想在每个进度结束时显示进度值每个进度条,

示例:最大进度 29,不要介意百分比值或下面的标签

经过一番搜索,我设法使进度条垂直,但我正在努力解决这个问题,

有没有办法在进度条的进度上附加一些东西?有没有办法引用xml中的进度? (最大值和不同项目的进度只在运行时知道,之后它们保持静态)

谢谢。

【问题讨论】:

    标签: android progress-bar


    【解决方案1】:

    使用普通进度条实现这一点并使其垂直会使一切变得过于复杂。所以我的解决方案是创建一个自定义视图,该视图由一个带有 ClipDrawable 背景的视图和一个 TextView 的标签组成。

    完整代码:

    LabeledVerticalProgressBar.java

    import android.content.Context;
    import android.content.res.ColorStateList;
    import android.content.res.TypedArray;
    import android.graphics.Typeface;
    import android.graphics.drawable.ClipDrawable;
    import android.graphics.drawable.ColorDrawable;
    import android.graphics.drawable.Drawable;
    import android.util.AttributeSet;
    import android.util.TypedValue;
    import android.view.Gravity;
    import android.view.View;
    import android.widget.TextView;
    
    import androidx.constraintlayout.widget.ConstraintLayout;
    
    import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
    import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
    import static androidx.constraintlayout.widget.ConstraintLayout.LayoutParams.PARENT_ID;
    
    public class LabeledVerticalProgressBar extends ConstraintLayout {
        private TextView textView;
        private View progressView;
    
        private Drawable progressDrawable;
        private float min;
        private float max;
        private float progress;
        private int labelTextAppearanceId;
        private boolean isLabelAbove = true;
        private int numDecimals;
        private String unit = "";
    
        private int height;
    
        public LabeledVerticalProgressBar(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(attrs);
        }
    
        private void init(AttributeSet attrs) {
            initViews();
            initAttributes(attrs);
            initProgress();
            initLabel();
        }
    
        private void initViews() {
            progressView = new View(getContext());
            progressView.setId(View.generateViewId());
            LayoutParams progressParams = new LayoutParams(MATCH_PARENT, MATCH_PARENT);
            progressParams.topToTop = PARENT_ID;
            progressParams.bottomToBottom = PARENT_ID;
            progressParams.startToStart = PARENT_ID;
            progressParams.endToEnd = PARENT_ID;
            progressView.setLayoutParams(progressParams);
    
            textView = new TextView(getContext());
            int padding = (int)(4 * getResources().getDisplayMetrics().density);
            textView.setPadding(padding, padding, padding, padding);
            LayoutParams textParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
            textParams.startToStart = progressView.getId();
            textParams.endToEnd = progressView.getId();
            textParams.bottomToBottom = progressView.getId();
            textView.setLayoutParams(textParams);
    
            addView(progressView);
            addView(textView);
    
            setClipChildren(false);
            setClipToPadding(false);
        }
    
        private void initAttributes(AttributeSet attrs) {
            TypedArray a = getContext().getTheme().obtainStyledAttributes(
                    attrs,
                    R.styleable.LabeledVerticalProgressBar,
                    0, 0);
            try {
                progressDrawable = a.getDrawable(R.styleable.LabeledVerticalProgressBar_progress_drawable);
                min = a.getFloat(R.styleable.LabeledVerticalProgressBar_min, 0);
                max = a.getFloat(R.styleable.LabeledVerticalProgressBar_max, 100);
                progress = a.getFloat(R.styleable.LabeledVerticalProgressBar_progress, 0);
    
                initTextAppearance(a);
    
                int labelPos = a.getInt(R.styleable.LabeledVerticalProgressBar_label_position, 0);
                if (labelPos == 1) {
                    isLabelAbove = false;
                }
                numDecimals = a.getInt(R.styleable.LabeledVerticalProgressBar_num_decimals, 0);
                unit = a.getString(R.styleable.LabeledVerticalProgressBar_unit);
    
                if (min >= max) {
                    throw new IllegalArgumentException("max should be greater than min");
                }
                clampProgress();
            } finally {
                a.recycle();
            }
        }
    
        private void initTextAppearance(TypedArray a) {
            /*TypedValue styleId = new TypedValue();
            boolean resolved = getContext().getTheme().resolveAttribute(R.styleable.LabeledVerticalProgressBar_label_text_appearance,
                    styleId, true);
            if (resolved) {
                labelTextAppearanceId = styleId.data;
            } else {
                labelTextAppearanceId = -1;
            }*/
            ColorStateList color = a.getColorStateList(R.styleable.LabeledVerticalProgressBar_label_text_color);
            int size = a.getDimensionPixelSize(R.styleable.LabeledVerticalProgressBar_label_text_size, -1);
            int style = a.getInt(R.styleable.LabeledVerticalProgressBar_label_text_style, -1);
            Drawable background = a.getDrawable(R.styleable.LabeledVerticalProgressBar_label_background);
    
            if (color != null) {
                textView.setTextColor(color);
            }
            if (size != -1) {
                textView.setTextSize(size);
            }
            if (style != -1) {
                switch (style) {
                    case 0: textView.setTypeface(textView.getTypeface(), Typeface.NORMAL); break;
                    case 1: textView.setTypeface(textView.getTypeface(), Typeface.BOLD); break;
                    case 2: textView.setTypeface(textView.getTypeface(), Typeface.ITALIC); break;
                    case 3: textView.setTypeface(textView.getTypeface(), Typeface.BOLD_ITALIC); break;
                }
            }
            if (background != null) {
                textView.setBackground(background);
            }
        }
    
        private void initProgress() {
            if (progressDrawable == null) {
                progressDrawable = initDefaultProgressDrawable();
            }
            updateProgressView();
        }
    
        @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
            height = progressView.getMeasuredHeight();
            initLabel();
            if (isLabelAbove) {
                // Add top space for label when progress is at max.
                setPadding(0, textView.getMeasuredHeight(), 0, 0);
            }
            super.onLayout(changed, left, top, right, bottom);
        }
    
        private void initLabel() {
            updateLabel();
            /*if (labelTextAppearanceId != -1) {
                TextViewCompat.setTextAppearance(textView, labelTextAppearanceId);
            }*/
        }
    
        public void setProgress(float progress) {
            this.progress = progress;
            clampProgress();
            progressView.getBackground().setLevel(computeLevel());
            updateLabel();
        }
    
        public void setProgress(final float progress, boolean animate, int duration) {
            if (animate) {
                float end = clampProgress(progress);
                float start = this.progress;
                ValueAnimator animator = ValueAnimator.ofFloat(start, end);
                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        LabeledVerticalProgressBar.this.progress = (float) animation.getAnimatedValue();
                        progressView.getBackground().setLevel(computeLevel());
                        updateLabel();
                    }
                });
                animator.setDuration(duration);
                animator.start();
    
            } else {
                setProgress(progress);
            }
        }
    
        public void setMax(float max) {
            this.max = max;
            if (min >= max) {
                throw new IllegalArgumentException("max should be greater than min");
            }
            setProgress(progress);
        }
    
        public void setMin(float min) {
            this.min = min;
            if (min >= max) {
                throw new IllegalArgumentException("max should be greater than min");
            }
            setProgress(progress);
        }
    
        public void setUnit(String unit) {
            this.unit = unit;
        }
    
        public void setProgressDrawableColor(int color) {
            progressDrawable = new ColorDrawable(color);
            updateProgressView();
        }
    
        public void setProgressDrawable(Drawable progressDrawable) {
            this.progressDrawable = progressDrawable;
            updateProgressView();
        }
    
        private void updateProgressView() {
            ClipDrawable clip = new ClipDrawable(progressDrawable, Gravity.BOTTOM, ClipDrawable.VERTICAL);
            progressView.setBackground(clip);
            clip.setLevel(computeLevel());
        }
    
        private void updateLabel() {
            float translation = computeLabelTranslation();
            if (isLabelAbove) {
                textView.setTranslationY(translation);
            } else {
                if (-translation > textView.getMeasuredHeight()) {
                    textView.setTranslationY(translation + textView.getMeasuredHeight());
                } else {
                    textView.setTranslationY(0);
                }
            }
            String progressStr = String.format("%." + numDecimals + "f", progress) + unit;
            textView.setText(progressStr);
        }
    
        private int computeLevel() {
            float fraction = computeProgressFraction();
            return (int) (fraction * 10000);
        }
    
        private float computeLabelTranslation() {
            return -computeProgressFraction() * height;
        }
    
        private float computeProgressFraction() {
            clampProgress();
            return (progress - min) / (max - min);
        }
    
        private Drawable initDefaultProgressDrawable() {
            int colorAttr = getContext().getResources().getIdentifier("colorPrimary",
                    "attr", getContext().getPackageName());
            TypedValue outValue = new TypedValue();
            getContext().getTheme().resolveAttribute(colorAttr, outValue, true);
            return new ColorDrawable(outValue.data);
        }
    
        private void clampProgress() {
            if (progress > max) {
                progress = max;
            } else if (progress < min) {
                progress = min;
            }
        }
    
        private float clampProgress(float progress) {
            if (progress > max) {
                return max;
            } else if (progress < min) {
                return min;
            } else {
                return progress;
            }
        }
    }
    

    attrs.xml

    <resources>
        <declare-styleable name="LabeledVerticalProgressBar">
            <attr name="progress_drawable" format="reference|color"/>
            <attr name="min" format="float"/>
            <attr name="max" format="float"/>
            <attr name="progress" format="float"/>
            <attr name="label_text_appearance" format="reference"/>
            <attr name="label_text_color" format="color"/>
            <attr name="label_text_size" format="dimension"/>
            <attr name="label_text_style" format="enum">
                <enum name="normal" value="0"/>
                <enum name="bold" value="1"/>
                <enum name="italic" value="2"/>
                <enum name="bold_italic" value="3"/>
            </attr>
            <attr name="label_background" format="reference|color"/>
            <attr name="label_position" format="enum">
                <enum name="above" value="0"/>
                <enum name="below" value="1"/>
            </attr>
    
            <!--Number of displayed decimal places for label-->
            <attr name="num_decimals" format="integer"/>
    
            <attr name="unit" format="string"/>
        </declare-styleable>
    </resources>
    

    用法:

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <marabillas.loremar.bindtextviewandprogressbar.LabeledVerticalProgressBar
            android:id="@+id/progressBar"
            android:layout_width="80dp"
            android:layout_height="480dp"
            android:background="#ddd"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:progress_drawable="#6a0dad"
            app:label_text_color="#fff"
            app:label_text_size="24sp"
            app:label_text_style="bold"
            app:label_background="#4ccc"
            app:label_position="below"
            app:min="20"
            app:max="500"
            app:progress="20"
            app:num_decimals="0"
            app:unit="%"/>
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
    @Override
        protected void onStart() {
            super.onStart();
            final LabeledVerticalProgressBar progressBar = findViewById(R.id.progressBar);
            progressBar.setProgress(500, true, 3000);
    
            Handler handler = new Handler(Looper.getMainLooper());
    
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    progressBar.setProgressDrawableColor(Color.GREEN);
                }
            }, 2000);
    
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    GradientDrawable gradient = new GradientDrawable();
                    int color1 = Color.HSVToColor(new float[]{240f, 1f, 1f});
                    int color2 = Color.HSVToColor(new float[]{240f, 1f, 0.5f});
                    int[] colors = { color1, color2, color1};
                    gradient.setColors(colors);
                    gradient.setGradientType(GradientDrawable.LINEAR_GRADIENT);
                    gradient.setOrientation(GradientDrawable.Orientation.LEFT_RIGHT);
                    progressBar.setProgressDrawable(gradient);
    
                    progressBar.setMax(2000);
                    progressBar.setProgress(4000);
                    progressBar.setMin(1000);
                    progressBar.setUnit("");
                    progressBar.setProgress(1500, true, 3000);
                }
            }, 3000);
        }
    

    结果:

    您可以使用setProgess() 设置不带动画的进度或将false 传递给animate 参数。

    就目前而言,我似乎无法为标签工作制作文本外观。

    我认为通过一些编辑,您也可以将其设为水平。

    【讨论】:

    • @Danny 我也很抱歉。我也没有足够的声誉来评论您的问题并要求澄清。因此,您希望将 textview 附加到进度的提示,对吗?据我所知,在 xml 中没有直接的方法可以做到这一点。您必须在代码中实现这一点。我很快就会更新我的解决方案。
    • 很好,我已经编辑了我的问题以更好地澄清我的问题,让我们去商店购买一些声誉;)
    • 很好的答案,我接受了,这几乎是我想要的,看看问题,我已经编辑了,因为我现在可以添加图像,一些问题希望你能回答我,我想以编程方式设置以下内容:最大进度 - 我在类中添加了一个 setMax 方法,我添加的进度的颜色: setProgressDrawable(int color) { DrawableCompat.wrap(progressDrawable).setTint(color); } 到班级,(那些很好吗?)并控制 xml 中的 textview 以更改我现在在班级的 initViews 方法中所做的一些属性,例如样式 bg 和大小,非常感谢。
    • @Danny 更新。这个类中可能还有更多要添加的内容,所以我将来可能会创建一个 github 项目并添加指向它的链接。或者更好的是,制作一个完整的 BarChartView。我真的很喜欢解决这个问题。从中得到了很多练习。谢谢。
    • 干得好,很高兴你喜欢这个,我接受了你的回答并赞成(轮到你了),我会使用它,我建议你将它发布到 jCenter。
    猜你喜欢
    • 2014-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-06
    • 2015-08-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多