前兩天改一個自定義翻頁滑動控件DefinedScrollView,爲了解決界面滑動衝突問題,當時使用場景是這樣的,本來設計的頁面是分作第一頁和第二頁,滑動翻頁,當時直接就用這個控件了,但是後來發現頁面內容太多,部分手機上第一頁根本顯示不全,爲了趕着上線我在第一頁裏面放入了scroolview 但是這樣造成了滑動衝突問題,我監聽了scroolview的事件強制傳遞給了父控件,用簡陋的方法解決了滑動衝突將app上線了,但是這樣畢竟體驗太差,所以還是得在控件上面做。改這個控件改了半天,基本實現了我要的功能,在這裏記錄相關的一些坑。
自定義控件的執行會先執行onMeasure方法,再執行onLayout方法。onMeasure中做佈局的測量,一般使用方法傳入的參數就可以了。下面是之前原作者的寫法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// ///final int width = MeasureSpec.getSize(widthMeasureSpec);
final int height = MeasureSpec.getSize(heightMeasureSpec);
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
// if (widthMode != MeasureSpec.EXACTLY) {
// throw new IllegalStateException(
// "ScrollLayout only canmCurScreen run at EXACTLY mode!");
// }
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
// if (heightMode != MeasureSpec.EXACTLY) {
// throw new IllegalStateException(
// "ScrollLayout only can run at EXACTLY mode!");
// }
// The children are given the same width and height as the scrollLayout
final int count = getChildCount();
for (int i = 0; i < count; i++) {
getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
}
scrollTo(0, mCurrentScreen * height);
}
其中他採用的也是我們使用的方法,這樣默認測量出來子佈局和父佈局寬高顯示是一樣的,如果我們將傳入的heightMeasureSpec改爲0,就是精確測量高度,在onLayout下我們就能獲得測量出的高度,做我們想要的處理。onLayout是控制佈局顯示,當我們調用measure後,就會觸發onMeasure和onLayout(onMeasure中調用子view的測量不會),如果在onLayout調用會導致無限循環!
子view通過layout方法控制顯示的區域,注意寬度如果採用精確測量,子view採用match_parent,不論layout寬度設置多大都是無法鋪滿全屏,這是一個大坑!
下面附上原作者寫的onLayout
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// /int childLeft = 0;
int childTop = 0;
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View childView = getChildAt(i);
if (childView.getVisibility() != View.GONE) {
// //final int childWidth = childView.getMeasuredWidth();
final int childHeight = childView.getMeasuredHeight();
/*
* childView.layout(childLeft, 0, childLeft + childWidth,
* childView.getMeasuredHeight());
*/
childView.layout(0, childTop, childView.getMeasuredHeight(),
childTop + childHeight);
childTop += childHeight;
// /childLeft += childWidth;
}
}
}
安卓中,LinearLayout、FrameLayout、RelativeLayout等佈局都繼承自ViewGroup,因此用ViewGroup做instanceof的話這幾種類型的View都會通過判斷。
自定義View有的構造函數重載有三個,建議這樣寫
public View(Context context) {
this(context, null);
}
public View(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public View(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
在最後一個重載裏面做初始化操作
自定義View中可以通過AttributeSet獲取一些自定義屬性,通過attr/attrs.xml裏面添加屬性
例如
<declare-styleable name="RoundProgressBar">
<attr name="roundColor" format="color" />
<attr name="roundProgressColor" format="color" />
<attr name="roundWidth" format="dimension"></attr>
<attr name="textColor" format="color" />
<attr name="textSize" format="dimension" />
<attr name="max" format="integer"></attr>
<attr name="textIsDisplayable" format="boolean"></attr>
<attr name="style">
<enum name="STROKE" value="0"></enum>
<enum name="FILL" value="1"></enum>
</attr>
<attr name="startText" format="string" />
<attr name="overText" format="string" />
</declare-styleable>
然後再構造函數中:
public RoundChartView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
paint = new Paint();
TypedArray mTypedArray = context.obtainStyledAttributes(attrs,
R.styleable.RoundProgressBar);
// 獲取自定義屬性和默認值
roundColor = mTypedArray.getColor(
R.styleable.RoundProgressBar_roundColor, Color.RED);
roundProgressColor = mTypedArray.getColor(
R.styleable.RoundProgressBar_roundProgressColor, Color.GREEN);
textColor = mTypedArray.getColor(
R.styleable.RoundProgressBar_textColor, Color.GREEN);
textSize = mTypedArray.getDimension(
R.styleable.RoundProgressBar_textSize, 15);
roundWidth = mTypedArray.getDimension(
R.styleable.RoundProgressBar_roundWidth, 5);
max = mTypedArray.getInteger(R.styleable.RoundProgressBar_max, 100);
textIsDisplayable = mTypedArray.getBoolean(
R.styleable.RoundProgressBar_textIsDisplayable, true);
style = mTypedArray.getInt(R.styleable.RoundProgressBar_style, 0);
starttext = mTypedArray
.getString(R.styleable.RoundProgressBar_startText);
overtext = mTypedArray.getString(R.styleable.RoundProgressBar_overText);
mTypedArray.recycle();
items = new ArrayList<RoundChartView.ChartItem>();
tempitems = new ArrayList<RoundChartView.ChartItem>();
}
再在佈局最外層加入
xmlns:app="http://schemas.android.com/apk/res/包名"
佈局中即可使用
<包名路徑.RoundProgressBar
android:id="@+id/roundProgressBar"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="10dp"
app:overText="已投滿"
app:roundColor="#d2f1ff"
app:roundProgressColor="#42c3ff"
app:roundWidth="4dp"
app:textColor="#42c3ff"
app:textSize="12dp" />