自定义ImageView属性赋值问题:timeInterval传递异常及动态属性配置
mTimeInterval Being 0 in onDraw() & Supporting Dynamic Attributes Let's break down your problem step by step and fix it properly.
Why is mTimeInterval 0 in onDraw()?
The core issue is how your constructors are set up:
- Your custom
ProgressBarView(Context context, int timeInterval)constructor assignsmTimeInterval, but this isn't called by Android's layout inflation system. If you declare the view in XML, the system uses theAttributeSet-enabled constructors, which don't initializemTimeInterval(so it stays at its default value of 0). - Even if you instantiate the view via code, configuration changes (like screen rotation) will trigger the
AttributeSetconstructor, resettingmTimeIntervalto 0.
Correct Ways to Pass & Update timeInterval
1. Support XML Attributes (Recommended for Layout Declarations)
First, add the time_interval attribute to your res/values/attrs.xml file:
<resources> <declare-styleable name="ProgressBarView"> <attr name="overlay_color" format="color"/> <!-- Add this attribute for time interval (unit: milliseconds) --> <attr name="time_interval" format="integer"/> </declare-styleable> </resources>
Update your init() method to read this attribute from XML, and remember to recycle the TypedArray to avoid memory leaks:
private void init(AttributeSet attrs) { if (attrs == null) { return; } TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.ProgressBarView); mColor = typedArray.getColor(R.styleable.ProgressBarView_overlay_color, Color.BLACK); // Read time_interval from XML, default to 0 if not specified mTimeInterval = typedArray.getInt(R.styleable.ProgressBarView_time_interval, 0); typedArray.recycle(); // Critical for memory management }
Now you can use the view in XML with the attribute (don't forget to add the app namespace to your root layout):
<com.yourpackage.ProgressBarView android:layout_width="100dp" android:layout_height="100dp" app:overlay_color="#FF0000" app:time_interval="60000"/> <!-- 60 seconds in milliseconds -->
2. Proper Constructor Chaining for Code Instantiation
Fix your constructors to ensure all paths initialize mTimeInterval correctly, supporting both code and XML use cases:
public ProgressBarView(Context context) { this(context, null); } // Custom constructor for code instantiation public ProgressBarView(Context context, int timeInterval) { this(context, null); mTimeInterval = timeInterval; } public ProgressBarView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ProgressBarView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(attrs); }
3. Dynamic Updates with a Setter Method
To change timeInterval after the view is created, add a setter method that triggers a redraw and resets the handler:
public void setTimeInterval(int timeInterval) { mTimeInterval = timeInterval; invalidate(); // Trigger immediate redraw // Cancel existing handler posts to avoid outdated updates mHandler.removeCallbacks(this::invalidate); updatePainting(); }
Bonus: Optimize Your onDraw() Code
Your current onDraw() creates a new Paint object every time, which is inefficient. Move the Paint initialization to init() and reuse it:
private Paint mPaint; private void init(AttributeSet attrs) { // ... existing code ... // Initialize paint once mPaint = new Paint(Paint.DITHER_FLAG); mPaint.setColor(mColor); mPaint.setStyle(Paint.Style.FILL); mPaint.setAlpha(205); // Note: Alpha ranges from 0 (transparent) to 255 (opaque) — -50 is invalid! }
Then use mPaint in onDraw() instead of creating a new instance each time.
内容的提问来源于stack exchange,提问作者hyhashemi

