Skip to content

Latest commit

 

History

History
251 lines (173 loc) · 7.37 KB

article.md

File metadata and controls

251 lines (173 loc) · 7.37 KB

一,原生RatingBar原理

先看一下RatingBar的继承关系

ProgressBar
	|_ AbsSeekBar
		|_ RatingBar

1,测量:

先来看看RatingBaronMeasure()

@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    if (mSampleWidth > 0) {
        // 计算宽度
        final int width = mSampleWidth * mNumStars;
        setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, 0),
                getMeasuredHeight());
    }
}

1.1 宽度计算

宽度是通过mSampleWidth乘以star数量得来,那么mSampleWidth又是怎么来的呢?

mSampleWidth定义在父类ProgressBar中:

// ProgressBar.java
// 定义
int mSampleWidth = 0;
// 构造方法
public ProgressBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
	
	final TypedArray a = context.obtainStyledAttributes(
			attrs, R.styleable.ProgressBar, defStyleAttr, defStyleRes);
	
	final Drawable progressDrawable = a.getDrawable(R.styleable.ProgressBar_progressDrawable);
	if (progressDrawable != null) {
		// Calling setProgressDrawable can set mMaxHeight, so make sure the
		// corresponding XML attribute for mMaxHeight is read after calling
		// this method.
		if (needsTileify(progressDrawable)) {
			setProgressDrawableTiled(progressDrawable);
		} else {
			setProgressDrawable(progressDrawable);
		}
	}
}


public void setProgressDrawableTiled(Drawable d) {
	if (d != null) {
		d = tileify(d, false);
	}

	setProgressDrawable(d);
}


private Drawable tileify(Drawable drawable, boolean clip) {
	...
	if (drawable instanceof BitmapDrawable) {
		final Drawable.ConstantState cs = drawable.getConstantState();
        // 根据Drawable获取BitmapDrawable
		final BitmapDrawable clone = (BitmapDrawable) cs.newDrawable(getResources());
		clone.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);

		if (mSampleWidth <= 0) {
            // 获取bitmap的宽度
			mSampleWidth = clone.getIntrinsicWidth();
		}
		...
	}
	return drawable;
}
// BitmapDrawable.java
@Override
public int getIntrinsicWidth() {
	return mBitmapWidth;
}

private void computeBitmapSize() {
	final Bitmap bitmap = mBitmapState.mBitmap;
	if (bitmap != null) {
		mBitmapWidth = bitmap.getScaledWidth(mTargetDensity);
		mBitmapHeight = bitmap.getScaledHeight(mTargetDensity);
	} else {
		mBitmapWidth = mBitmapHeight = -1;
	}
}

因此RatingBar的宽度只跟图片的宽度相关。

1.2 高度计算

RatingBar没有自己计算高度直接调用getMeasuredHeight() ,因此高度的计算是在父类中完成。

父类AbsSeekBaronMeasure()实现如下:

@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // 注意此处并没有调用super.onMeasure();
	Drawable d = getCurrentDrawable();

	int thumbHeight = mThumb == null ? 0 : mThumb.getIntrinsicHeight();
	int dw = 0;
	int dh = 0;
	if (d != null) {
		dw = Math.max(mMinWidth, Math.min(mMaxWidth, d.getIntrinsicWidth()));
        // getIntrinsicHeight()获取到的是drawable的原始高度
		dh = Math.max(mMinHeight, Math.min(mMaxHeight, d.getIntrinsicHeight()));
		dh = Math.max(thumbHeight, dh);
	}
	dw += mPaddingLeft + mPaddingRight;
	dh += mPaddingTop + mPaddingBottom;

	setMeasuredDimension(resolveSizeAndState(dw, widthMeasureSpec, 0),
			resolveSizeAndState(dh, heightMeasureSpec, 0));
}

因此父类AdbSeekBar是根据drawable的原始高度及minHeightmaxHeight进行计算的。

1.3 小结

RatingBar的宽度只跟图片的大小相关,高度和图片大小以及minHeightmaxHeight相关,不能根据设置的宽高自适应。那么这些图片的宽高是多少呢?

RatingBar提供了三种样式,可以从整个app的themeTheme.MaterialComponents.Light.DarkActionBar开始寻找

<!-- 默认样式 -->
<style name="Base.Widget.AppCompat.RatingBar" parent="android:Widget.RatingBar">
	<item name="android:progressDrawable">@drawable/abc_ratingbar_material</item>
	<item name="android:indeterminateDrawable">@drawable/abc_ratingbar_material</item>
</style>

<!-- Indicator样式 -->
<style name="Base.Widget.AppCompat.RatingBar.Indicator" parent="android:Widget.RatingBar">
	<item name="android:progressDrawable">@drawable/abc_ratingbar_indicator_material</item>
	<item name="android:indeterminateDrawable">@drawable/abc_ratingbar_indicator_material</item>
	<item name="android:minHeight">36dp</item>
	<item name="android:maxHeight">36dp</item>
	<item name="android:isIndicator">true</item>
	<item name="android:thumb">@null</item>
</style>

<!-- Small样式 -->
<style name="Base.Widget.AppCompat.RatingBar.Small" parent="android:Widget.RatingBar">
	<item name="android:progressDrawable">@drawable/abc_ratingbar_small_material</item>
	<item name="android:indeterminateDrawable">@drawable/abc_ratingbar_small_material</item>
	<item name="android:minHeight">16dp</item>
	<item name="android:maxHeight">16dp</item>
	<item name="android:isIndicator">true</item>
	<item name="android:thumb">@null</item>
</style>

其中minHeightmaxHeight可以控制控件的高度,而progressDrawable设置的图片控制着星星的大小。他们的大小分别是48dp,36dp,16dp。

注意:以上测量出来的宽高度只能控制这个view的大小,并不能改变内部星星的显示大小。

2,绘制:

最后,说一个比较复杂的Drawable,是进度条相关的。

LayerDrawable:对应Seekbar android:progressDrawable

通常,我们用XML定义一个进度条的ProgressDrawable是这样的,

<!--ProgressDrawable-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@android:id/background" android:drawable="@drawable/background"/>
    <item android:id="@android:id/secondaryProgress" android:drawable="@drawable/secondary_progress"/>
    <item android:id="@android:id/progress" android:drawable="@drawable/progress"/>
</layer-list>

而对于其中的,@drawable/progress@drawable/secondary_progress也不是普通的drawable,

<!--@drawable/progress 定义-->
<clip xmlns:android="http://schemas.android.com/apk/res/android"
      android:clipOrientation="horizontal"
      android:drawable="@drawable/progress_drawable"
      android:gravity="left" >
</clip>

也就是说,通过XML要定义进度条的ProgressDrawable,我们需要定义多个XML文件的,还是比较复杂的。那么JavaCode实现呢?

其实,理解了XML实现的方式,下面的JavaCode就很好理解了。

LayerDrawable layerDrawable = (LayerDrawable) getProgressDrawable();

//背景
layerDrawable.setDrawableByLayerId(android.R.id.background, backgroundDrawable);

//进度条
ClipDrawable clipProgressDrawable = new ClipDrawable(progressDrawable, Gravity.LEFT, ClipDrawable.HORIZONTAL);
layerDrawable.setDrawableByLayerId(android.R.id.progress, clipProgressDrawable);

//缓冲进度条
ClipDrawable clipSecondaryProgressDrawable = new ClipDrawable(secondaryProgressDrawable, Gravity.LEFT, ClipDrawable.HORIZONTAL);
layerDrawable.setDrawableByLayerId(android.R.id.secondaryProgress, clipSecondaryProgressDrawable);

二,AndRatingBar原理

https://mp.weixin.qq.com/s?__biz=MzI1NjEwMTM4OA==&mid=2651232105&idx=1&sn=fcc4fa956f329f839f2a04793e7dd3b9&mpshare=1&scene=21&srcid=0719Nyt7J8hsr4iYwOjVPXQE#wechat_redirect