目標(biāo):
1、掌握自定義view的流程
2、掌握自定義view的三個方法
3喜最、掌握自定義view實(shí)現(xiàn)方式
4俐末、掌握自定義view屬性設(shè)置
一料按、xml的實(shí)質(zhì)
- xml不是必須,布局可以代碼寫
- xml是為了開發(fā)者開發(fā)布局便利,谷歌給開發(fā)者開發(fā)糖
- xml最終還是會轉(zhuǎn)成代碼執(zhí)行
二.View和ViewGroup
- View
1.1 View是用戶界面一個組件(控件)
1.2 View是一個矩形
1.3.View的職責(zé)是繪制和事件處理
- ViewGroup
2.1 ViewGroup是一個特殊的View,它繼承自View
2.2 ViewGroup可包含其他View(孩子)
2.3.ViewGroup常用layout的基類
2.4 ViewGroup定義了孩子的布局參數(shù)(帶layout_前綴的屬性)
-
View和ViewGroup的關(guān)系
繼承關(guān)系
view_arc.png
組合關(guān)系
三.什么是自定義控件?
- 原生控件:SDK已經(jīng)有卓箫,Google提供
- 自定義控件: 開發(fā)者自己開發(fā)的控件,分三種
a. 組合式控件:將現(xiàn)有控件進(jìn)行組合载矿,實(shí)現(xiàn)功能更加強(qiáng)大控件;
b. 繼承現(xiàn)有控件: 對其控件的功能進(jìn)行拓展;
c. 重寫View(ViewGroup)實(shí)現(xiàn)全新的控件.
四.為什么要自定義View?
原有控件無法滿足我們的需求,所以需要自己實(shí)現(xiàn)想要的效果。
五.組合式控件下拉選擇框
模塊化思想烹卒,提高代碼復(fù)用率
功能分析:
a. 點(diǎn)擊箭頭闷盔,彈出下拉列表 (Popupwindow + ListView)
b. 點(diǎn)擊列表選項(xiàng),在編輯框中顯示內(nèi)容實(shí)現(xiàn)步驟:
a.繼承布局,重寫構(gòu)造;
b.創(chuàng)建布局xml旅急,加入到自定義控件里面;實(shí)現(xiàn)對應(yīng)的功能.
xml布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/et"
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="50dp" />
<ImageView
android:id="@+id/iv"
android:layout_alignParentRight="true"
android:layout_marginRight="10dp"
android:layout_marginTop="10dp"
android:src="@drawable/down_arrow"
android:layout_width="50dp"
android:layout_height="50dp" />
</RelativeLayout>
自定義view代碼:
public class SpinnerView extends RelativeLayout {
private ImageView iv_arrow;
private EditText et_content;
private ArrayList<String> list;
private PopupWindow pop;
public SpinnerView(Context context) {
super(context);
}
public SpinnerView(Context context, AttributeSet attrs) {
super(context, attrs);
initData();
initView(context);
}
private void initData() {
list = new ArrayList<>();
for (int i = 0; i < 20; i++) {
list.add("數(shù)據(jù)" + i);
}
}
private void initView(Context context) {
View view = LayoutInflater.from(context).inflate(R.layout.layout_spinner, null);
this.addView(view);
iv_arrow = view.findViewById(R.id.iv_arrow);
et_content = view.findViewById(R.id.et_content);
iv_arrow.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
showPop();
}
});
}
private void showPop() {
if (pop == null) {
pop = new PopupWindow(et_content.getWidth(), ViewGroup.LayoutParams.WRAP_CONTENT);
}
final ListView listView = new ListView(getContext());
listView.setAdapter(new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, list));
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String s = list.get(position);
et_content.setText(s);
et_content.setSelection(s.length());
pop.dismiss();
}
});
pop.setContentView(listView);
pop.setBackgroundDrawable(new ColorDrawable());
pop.setOutsideTouchable(true);
pop.showAsDropDown(iv_arrow);
}
}
六逢勾、繼承現(xiàn)有控件
實(shí)現(xiàn)帶有刪除線的TextView
public class DeleteTextview extends TextView {
public DeleteTextview(Context context) {
super(context);
}
public DeleteTextview(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public DeleteTextview(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setDeleteLine(){
getPaint().setFlags(Paint.STRIKE_THRU_TEXT_FLAG|Paint.ANTI_ALIAS_FLAG);
}
}
七、View的繪制流程
a藐吮、Measure測量一個View的大小 (onMeasure)
b溺拱、Layout擺放一個View的位置 (onLayout)
c、Draw畫出View的顯示內(nèi)容 (onDraw)
其中measure是final的谣辞,無法重寫迫摔,雖然layout,draw不是final的,但是也不建議重寫該方法泥从。
這三個方法都已經(jīng)寫好了View的邏輯攒菠,如果我們想實(shí)現(xiàn)自身的邏輯,而又不破壞View的工作流程歉闰,可以重寫onMeasure辖众、onLayout、onDraw方法和敬。
/**
* 代碼操作的時候凹炸,直接new對象
* @param context
*/
public CView(Context context) {
super(context);
}
/**
* 布局文件中設(shè)置屬性
* @param context
* @param attrs
*/
public CView(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* 自定義屬性
* @param context
* @param attrs
* @param defStyleAttr
*/
public CView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
7.1 View的測量(measure)
measure()--->onMeasure(實(shí)際測量工作)--->setMeasuredDimension(保存測量好的寬高)-->setMeasuredDimensionRaw(設(shè)置寬高)
Android系統(tǒng)在繪制View之前,必須對View進(jìn)行測量昼弟,即告訴系統(tǒng)該畫一個多大的View啤它,這個過程在onMeasure()方法中進(jìn)行。
MeasureSpec類(onMeasure)
Android系統(tǒng)給我們提供了一個設(shè)計小而強(qiáng)的工具類--MeasureSpec類
1、MeasureSpec描述了父View對子View大小的期望变骡。里面包含了測量模式和大小离赫。
2、MeasureSpec類把測量模式和大小組合到一個32位的int型的數(shù)值中塌碌,其中高2位表示模式渊胸,低30位表示大小而在計算中使用位運(yùn)算的原因是為了提高并優(yōu)化效率。
3台妆、我們可以通過以下方式從MeasureSpec中提取模式和大小翎猛,該方法內(nèi)部是采用位移計算。
也可以通過MeasureSpec的靜態(tài)方法把大小和模式合成接剩,該方法內(nèi)部只是簡單的相加切厘。
- 測量模式 -- 在對View進(jìn)行測量時,Android提供了三種測量模式:
EXACTLY
即 精確值模式 懊缺,當(dāng)控件的layout_width屬性或layout_height屬性指定為具體數(shù)值時疫稿,例如android:layout_width="100dp",或者指定為match_parent屬性時鹃两,系統(tǒng)使用的是EXACTLY 模式遗座。AT_MOST
即 最大值模式 ,當(dāng)控件的layout_width屬性或layout_height屬性指定為warp_content時怔毛,控件大小一般隨著控件的子控件或者內(nèi)容的變化而變化员萍,此時控件的尺寸只要不超過父控件允許的最大尺寸即可。UNSPECIFIED
這個屬性很奇怪拣度,因?yàn)樗恢付ㄆ浯笮y量的模式碎绎,View想多大就多大,通常情況下在繪制自定義View時才會使用抗果。
- View默認(rèn)的onMeasure()方法只支持EXACTLY模式筋帖,所以如果在自定義控件的時候不重寫onMeasure()方法的話,就只能使用EXACTLY模式冤馏,且控件只可以響應(yīng)你指定的具體寬高值或者是match_parent屬性日麸。如果要讓自定義的View支持wrap_content屬性,那么就必須重寫onMeasure()方法來指定wrap_content時的大小逮光。
而通過上面介紹的MeasureSpec這個類代箭,我們就可以獲取View的測量模式和View想要繪制的大小(可以打印日志獲忍楦铡)嗡综。
7.2View的布局(layout)
layout布局流程圖:
layout()
a. layout方法中接受四個參數(shù),是由父View提供杜漠,指定了子View在父View中的左极景、上察净、右、下的位置盼樟。父View在指定子View的位置時通常會根據(jù)子View在measure中測量的大小來決定氢卡。
b. 子View的位置通常還受有其他屬性左右,例如父View的orientation晨缴,gravity译秦,自身的margin等等,特別是RelativeLayout喜庞,影響布局的因素非常多诀浪。
c. layout方法雖然可以被復(fù)寫,但是不建議去復(fù)寫,我們可以直接調(diào)用layout方法去確定自身的位置, 而且可以去復(fù)寫onLayout方法去確定子view的位置
setFrame()
a. setFrame方法是一個隱藏方法棋返,所以作為應(yīng)用層程序員來說延都,無法重寫該方法。該方法體內(nèi)部通過比對本次的l睛竣、t晰房、r、b四個值與上次是否相同來判斷自身的位置和大小是否發(fā)生了改變射沟。
b. 如果發(fā)生了改變殊者,將會調(diào)用invalidate請求重繪。
c. 記錄本次的l验夯、t猖吴、r、b挥转,用于下次比對海蔽。
d. 如果大小發(fā)生了變化,onSizeChanged方法绑谣,該方法在大多數(shù)View中都是空實(shí)現(xiàn)党窜,我們可以重寫該方法用于監(jiān)聽View大小發(fā)生變化的事件,在可以滾動的視圖中重載了該方法借宵,用于重新根據(jù)大小計算出需要滾動的值幌衣,以便顯示之前顯示的區(qū)域。
onLayout()
a. onLayout是ViewGroup用來決定子View擺放位置的壤玫,各種布局的差異都在該方法中得到了體現(xiàn)豁护。
b. onLayout比layout多一個參數(shù),changed欲间,該參數(shù)是在setFrame通過比對上次的位置得出是否發(fā)生了變化楚里,通常該參數(shù)沒有被使用的意義,因?yàn)楦竀iew位置和大小不變括改,并不能代表子View的位置和大小沒有發(fā)生改變腻豌。
7.3View的繪制(draw)
draw方法繪制要遵循一定的順序:
1.畫背景
2.畫邊緣
3.畫自身: ondraw方法
4.畫子View: dispatchDraw方法
6.畫滾動條
draw繪制流程:
draw()
draw是由ViewRoot的performTraversals方法發(fā)起家坎,它將調(diào)用DecorView的draw方法,并把成員變量canvas傳給給draw方法吝梅。而在后面draw遍歷中虱疏,傳遞的都是同一個canvas。所以android的繪制是同一個window中的所有View都繪制在同一個畫布上苏携。等繪制完成做瞪,將會通知WMS把canvas上的內(nèi)容繪制到屏幕上。自定義View時一般不重寫該方法右冻。
onDraw()
a. View用來繪制自身的實(shí)現(xiàn)方法装蓬,如果我們想要自定義View,通常需要重載該方法纱扭。
b. 比如TextView中在該方法中繪制文字牍帚、光標(biāo)和CompoundDrawable,
ImageView中相對簡單,只是繪制了圖片
八.繪制實(shí)戰(zhàn)
1. 畫直線
int startX = 5;
int startY = 100;
int stopX = 195;
int stopY = 100;
canvas.drawLine(startX, startY, stopX, stopY, mPaint);
2. 畫圓
canvas.drawCircle(100, 100, 40, mPaint);
3.畫空心圓
//還是畫圓,改動畫筆即可
mPaint.setAntiAlias(true);//去鋸齒
mPaint.setStyle(Style.STROKE);//改變style可以畫出空心圓
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.GREEN);
4. 畫圖片
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
5. 畫三角形(多邊形)
mPath = new Path();
//規(guī)劃三角形的路徑
int x1 = 100, y1 = 5;
int x2 = 195, y2 = 195;
int x3 = 5, y3 = 195;
mPath.moveTo(x1, y1);
//連接第一個點(diǎn)和第二個點(diǎn)
mPath.lineTo(x2, y2);
mPath.lineTo(x3, y3);
mPath.lineTo(x1, y1);
canvas.drawPath(mPath, mPaint);
6.裁剪
canvas.clipPath(mPath);//先裁切再畫別的東西,比如圖片
7. 畫扇形(放在拆剪后邊畫不出來)
mOval = new RectF(5, 5, 195, 195);
int startAngle = -90;
int sweepAngle = 60;
boolean useCenter = false;//是否畫扇形的兩條邊
canvas.drawArc(mOval, startAngle, sweepAngle, useCenter, mPaint);
九.ViewGroup的繪制流程
ViewGroup繼承View, ViewGroup的繪制流程遵循View的繪制的流程
1. ViewGroup的測量
相同點(diǎn):measure - > onMeasure
不同點(diǎn):作為一個父容器乳蛾,有責(zé)任去測量孩子,調(diào)用孩子measure方法暗赶,傳入期望
2. ViewGroup的布局
相同點(diǎn):layout(ViewGroup父容器發(fā)起布局)
不同點(diǎn):作為一個父容器,有責(zé)任去布局孩子肃叶,在onLayout方法里面蹂随,調(diào)用孩子layout方法,指定孩子上下左右的位置
3. ViewGroup的繪制
相同點(diǎn):draw -> onDraw
不同點(diǎn):ViewGroup默認(rèn)實(shí)現(xiàn)dispatchDraw方法去繪制了孩子
4. getWidth和getMeasuredWidth的區(qū)別
a. getMeasuredWidth:獲取測量后寬高
b. getWidth:獲取布局之后的寬高
十.案例
1.小圓球
實(shí)現(xiàn)的功能:手指在屏幕上滑動因惭,紅色的小球始終跟隨手指移動岳锁。
實(shí)現(xiàn)的思路:
1)自定義View,在onDraw中畫圓作為小球蹦魔;
2)重寫自定義View的onTouchEvent方法激率,記錄觸屏坐標(biāo),用新的坐標(biāo)重新繪制小球版姑;
3)在布局中引用自定義View布局柱搜,運(yùn)行程序,實(shí)現(xiàn)跟隨手指移動效果剥险。關(guān)鍵技術(shù)點(diǎn):自定義View應(yīng)用聪蘸、觸摸事件處理、canvas繪圖表制、Paint應(yīng)用健爬。
public class BallView extends View {
private final Bitmap mBitmap;
private final Paint mPaint;
private float mMoveX = 60;
private float mMoveY = 60;
public BallView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ball);
mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setAntiAlias(true);
}
@Override
protected void onDraw(Canvas canvas) {
//1.畫小球(圖片) 使用觸摸時的位置作為左上角
//canvas.drawBitmap(mBitmap, mMoveX, mMoveY, mPaint);
//2.畫圓,使用了觸摸屏幕時的位置作為圓心
canvas.drawCircle(mMoveX,mMoveY,60,mPaint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//獲取觸摸屏幕時觸摸點(diǎn)的位置
mMoveX = event.getX();
mMoveY = event.getY();
//重新繪制界面
invalidate();
return true;
}
}
2.圓環(huán)進(jìn)度條
- 對于自定義view,很多時候需要使用到自定義屬性么介,我們向?qū)崿F(xiàn)一個view的自定義屬性娜遵,需要遵循以下幾部:
a.自定義一個CustomView(extends View )類
b.編寫values/attrs.xml,在其中編寫styleable和item等標(biāo)簽元素
c.在布局文件中CustomView使用自定義的屬性(注意namespace)
導(dǎo)入自定義屬性壤短,以下兩種方式都可(namespace)
- http://schemas.android.com/apk/res/包名
- xmlns:app="http://schemas.android.com/apk/res-auto"(xml命名空間设拟,ns代表name space)
d.在CustomView的構(gòu)造方法中通過TypedArray獲取
2.1 AttributeSet與TypedArray
?構(gòu)造方法中的有個參數(shù)叫做AttributeSet(eg: MyTextView(Context context, AttributeSet attrs) )這個參數(shù)看名字就知道包含的是參數(shù)的集合慨仿,那么我能不能通過它去獲取我的自定義屬性呢?
?首先AttributeSet中的確保存的是該View聲明的所有的屬性纳胧,并且外面的確可以通過它去獲攘骸(自定義的)屬性,怎么做呢跑慕?
?其實(shí)看下AttributeSet的方法就明白了万皿,下面看備注1的代碼及打印結(jié)果。
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
int count = attrs.getAttributeCount();
for (int i = 0; i < count; i++) {
String attrName = attrs.getAttributeName(i);
String attrVal = attrs.getAttributeValue(i);
Log.e(TAG, "attrName = " + attrName + " , attrVal = " + attrVal);
}
// ==>use typedarray ...
}
?通過AttributeSet獲取的值核行,如果是引用都變成了@+數(shù)字的字符串牢硅。你說,這玩意你能看懂么芝雪?那么你看看最后一行使用TypedArray獲取的值减余,是不是瞬間明白了什么。
?TypedArray其實(shí)是用來簡化我們的工作的绵脯,比如上例佳励,如果布局中的屬性的值是引用類型(比如:@dimen/dp100)休里,如果使用AttributeSet去獲得最終的像素值蛆挫,那么需要第一步拿到id,第二步再去解析id妙黍。而TypedArray正是幫我們簡化了這個過程悴侵。
貼一下:如果通過AttributeSet獲取最終的像素值的過程:
int widthDimensionId = attrs.getAttributeResourceValue(0, -1);
Log.e(TAG, "layout_width= "+getResources().getDimension(widthDimensionId));
ok,現(xiàn)在別人問你TypedArray存在的意義拭嫁,你就可以告訴他了可免。
代碼:
values/attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyProgressBar">
<!--使用系統(tǒng)的寬高屬性-->
<attr name="android:layout_width"/>
<attr name="android:layout_height"/>
<!--自定義的-->
<!--外環(huán)顏色-->
<attr name="ringColor" format="color"/>
<!--外環(huán)寬度-->
<attr name="ringWidth" format="dimension"/>
<!--內(nèi)圓顏色-->
<attr name="circleColor" format="color"/>
<!--進(jìn)度文本大小顏色-->
<attr name="android:textSize"/>
<attr name="android:textColor"/>
<!--進(jìn)度 扇形的掃過角度-->
<attr name="sweepAngle" format="integer"/>
<attr name="startAngle" format="integer"/>
</declare-styleable>
</resources>
自定義View:
public class MyProgressBar extends View {
private static final String TAG = "MyProgressBar";
private float mHeight;
private float mWidth;
private int mRingColor;
private float mRingWidth;
private int mCircleColor;
private float mTextSize;
private int mTextColor;
private int mStartAngle;
private int mSweepAngle;
private float mRadius;
private float mCenterXY;
private Paint mCirclePaint;
private Paint mTextPaint;
private String mProgress;
private RectF mRectF;
private Paint mSweepPaint;
private final float mDy;
public MyProgressBar(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
/*for (int i = 0; i < attrs.getAttributeCount(); i++) {
String attributeName = attrs.getAttributeName(i);
//如果是引用類型的話,attributeValue就變成了@+數(shù)字的字符串
String attributeValue = attrs.getAttributeValue(i);
//getResources().getColor(attributeValue)
//getResources().getString(attributeValue)
Log.d(TAG, "MyProgressBar: "+"attributeName:"+attributeName+",attributeValue"+attributeValue);
}*/
//系統(tǒng)提供的一個幫助類,可以直接過去里面的屬性值,就算是引用類型的,也可以直接獲取
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyProgressBar);
if (ta != null){
//獲取屬性
mHeight = ta.getDimension(R.styleable.MyProgressBar_android_layout_height, 200);
mWidth = ta.getDimension(R.styleable.MyProgressBar_android_layout_width, 200);
mRingColor = ta.getColor(R.styleable.MyProgressBar_ringColor, 0);
mRingWidth = ta.getDimension(R.styleable.MyProgressBar_ringWidth, 10);
mCircleColor = ta.getColor(R.styleable.MyProgressBar_circleColor, 0);
mTextSize = ta.getDimension(R.styleable.MyProgressBar_android_textSize, 16);
mTextColor = ta.getColor(R.styleable.MyProgressBar_android_textColor, 0);
mStartAngle = ta.getInteger(R.styleable.MyProgressBar_startAngle, -90);
mSweepAngle = ta.getInteger(R.styleable.MyProgressBar_sweepAngle, 0);
ta.recycle();//釋放資源
}
//獲取圓的畫筆
mCirclePaint = new Paint();
mCirclePaint.setColor(mCircleColor);
mCirclePaint.setAntiAlias(true);//設(shè)置畫筆抗鋸齒
//獲取文本畫筆
mTextPaint = new Paint();
mTextPaint.setColor(mTextColor);
mTextPaint.setTextSize(mTextSize);
mTextPaint.setTextAlign(Paint.Align.CENTER);//設(shè)置文本對齊方式
//獲取繪制文本對象
Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
float v1 = fontMetrics.descent - fontMetrics.ascent;
mDy = v1/2-fontMetrics.descent;
float v = mSweepAngle * 1.0f / 360 * 100;
mProgress = (int)v+" %";
//獲取圓環(huán)畫筆
mSweepPaint = new Paint();
mSweepPaint.setAntiAlias(true);
mSweepPaint.setColor(mRingColor);
mSweepPaint.setStrokeWidth(mRingWidth);
mSweepPaint.setStyle(Paint.Style.STROKE);//繪制圓環(huán)
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int max = (int) Math.max(mWidth, mHeight);
setMeasuredDimension(max,max);
//setMeasuredDimension((int) mWidth,(int)mHeight);
mRadius = max*1.0f/4;
mCenterXY = max *1.0f/2;
float v = max * 0.9f;
mRectF = new RectF(max*0.1f, max*0.1f,v , v);
}
@Override
protected void onDraw(Canvas canvas) {
//1.環(huán)里面的圓
canvas.drawCircle(mCenterXY,mCenterXY,mRadius,mCirclePaint);
//2.畫文字
canvas.drawText(mProgress,mCenterXY,mCenterXY+mDy,mTextPaint);
//3.畫進(jìn)度,扇形
canvas.drawArc(mRectF,mStartAngle,mSweepAngle,false,mSweepPaint);
}
/**
*
* @param progress 0-100
*/
public void setProgress(int progress) {
mSweepAngle =(int)(progress*3.6f) ;
mProgress = progress+" %";
invalidate();//只能在ui線程中調(diào)用
//postInvalidate();// 可以在非ui線程中調(diào)用
}
}
布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xts="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.edu.cumulus.balldemo.MainActivity">
<com.edu.cumulus.balldemo.MyProgressBar
android:id="@+id/myProgressBar"
android:background="#55000000"
xts:circleColor="@color/colorAccent"
xts:ringColor="@color/colorPrimary"
xts:ringWidth="20dp"
xts:sweepAngle="89"
xts:startAngle="-90"
android:text="@string/app_name"
android:textSize="20sp"
android:textColor="#ffffff"
android:layout_width="300dp"
android:layout_height="300dp" />
<Button
android:id="@+id/btn"
android:text="開始"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
MainActivity使用,點(diǎn)擊按鈕,模擬下載:
private void start() {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
mMyProgressBar.setProgress(i);
}
}
}).start();
}