三三要成为安卓糕手
一:ProgressBar
ProgressBar有很多种进度的样式,比如环形加载,水平横向进度条加载
1:环形
不用style,默认显示为环形
使用Wrap也可以显示出来,是因为它本身内部就有一定的大小
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="100dp"
android:layout_height="100dp"
android:indeterminate="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="加载中"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@id/progress_bar"
app:layout_constraintLeft_toLeftOf="@id/progress_bar"
app:layout_constraintRight_toRightOf="@id/progress_bar"
app:layout_constraintTop_toTopOf="@id/progress_bar" />
(1)indeterminate
不带真实进度的进度条,叫不确定模式
- 若未设置
android:indeterminate
,默认值是false
进度条会显示确定模式(即展示具体进度值,如 0%~100%)。 - 若设置为
android:indeterminate="true"
,则会切换为不确定模式(循环动画,不显示具体进度)
注:在一些Android版本下,旋转的ProgressBar通常直接与不确定模式相关联,所以哪怕改成确定模式,也依然无效,如果想实现环形带进度的样式,需要使用到更高级的开发手段
左图为true,右图为false
2:水平横向型
(1)style
其实style不是单个属性奥,是为当前控件去指定一系列属性的集合
<ProgressBar
android:id="@+id/progress_bar_horizontal"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="false"
android:max="200"
android:progress="30"
android:secondaryProgress="60"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
水平的
(2)indeterminateOnly
这里是我们使用的一种style,源码如下;
indeterminateOnly:限制为不确定模式,false;代表我们可以做一些进度的处理
(3)progress默认进度
(4)max最大进度
(5)sencondaryProgress
二:使用进度条记录下载进度
1:需求分析
需求:现在有一个页面,我们需要有一个实时的进度条往前走,展示下载文件的下载进度
2:前端设计
<ProgressBar
android:id="@+id/pb_load"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="100"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.7" />
<Button
android:id="@+id/btn_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="开始下载"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/pb_load" />
<TextView
android:id="@+id/tv_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="当前下载进度:0%"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/pb_load" />
3:后端Java线程操作
for循环,线程模拟进度条;每循环1次,线程休眠一次
public class ProgressBarActivity extends AppCompatActivity {
private ProgressBar pbload;
private TextView tvProgress;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_progress_bar);
pbload = findViewById(R.id.pb_load);
tvProgress = findViewById(R.id.tv_progress);
findViewById(R.id.btn_start).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
pbload.setProgress(0);
startLoad();
}
});
}
public void startLoad(){
Thread thread = new Thread(){
@Override
public void run(){
for (int i = 0; i <= 100; i++) {
try {
Thread.sleep(100);
int finalI = i;
runOnUiThread(new Runnable() {
@Override
public void run() {
pbload.setProgress(finalI);
tvProgress.setText("当前下载进度:" + finalI + "%");
}
});
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
};
thread.start();
}
}
三:代码分析
1:声明成员变量
下载进度需要实时更新,声明为成员变量
2:runOnUiThread
(1)安卓中的主线程
在主线程中执行任务,它和 Java 里 main 函数不是一码事,
简单说:主线程 = UI 线程,是 Android 专门用来渲染界面、更新 UI 的线程 。
APP 启动时,系统自动创建,所有和 “界面显示、控件更新(比如 TextView.setText
、ProgressBar.setProgress
)” 相关的操作,必须在这线程里执行,不然会报错!
(2)如何切回主线程修改UI
在我们的代码中每次循环,都用 runOnUiThread(new Runnable() { ... })
作用是:把 Runnable
里的代码,丢回主线程执行!这样 pbLoad.setProgress(...)
、tvProgress.setText(...)
才能安全更新界面,不报错。
总结:在Android当中,所有页面更新的相关操作,都要在主线程中实现
3:细节处理
每次点击开始下载按钮时,先把进度初始化为0
效果如下
四:Seekbar
1:样式
样式如下;主要用于调节音量,亮度等;默认最大进度为100,progress和max就不多介绍了;主要用法还是在java代码上
<SeekBar
android:id="@+id/sb_volume"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:max="100"
android:progress="10"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/tv_volume"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="当前音量:10%"
app:layout_constraintTop_toBottomOf="@+id/sb_volume"
app:layout_constraintStart_toStartOf="parent"/>
2:继承关系
五:联动Seekbar与TextView
1:Java代码
public class ProgressBarActivity2 extends AppCompatActivity {
private static final String TAG = "ProgressBarActivity2";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_progress_bar2);
SeekBar seekBar = findViewById(R.id.sb_volume);
TextView tvVolume = findViewById(R.id.tv_volume);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener(){
/**
* 进度被改变时,调用
*/
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
Log.i(TAG, "onProgressChanged: " + progress);
tvVolume.setText("当前音量:" + progress + "%");
}
/**
* 开始拖动进度条时,调用
*/
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
Log.i(TAG, "onStartTrackingTouch: 开始拖动了" );
}
/**
* 用户停止拖拽的时候,调用
* @param seekBar The SeekBar in which the touch gesture began
*/
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
Log.i(TAG, "onStopTrackingTouch: 停止拖动");
}
});
}
}
2:SeekBar监听器
setOnSeekBarChangeListener —> 设置一个监听器
new SeekBar.OnSeekBarChangeListener() 需要重写三个方法
(1)onProgressChanged
进度被改变时,调用
(2)onStartTrackingTouch
开始拖动进度条时,调用
(3)onStopTrackingTouch
用户停止拖拽的时候,调用
3:日志打印分析
下面的日志就非常的清楚了,这三个方法的作用
六:收获
学了ProgressBar和SeekBar两种进度条
ProgressBar两种样式,Java中更新UI控件需要再主线程中修改代码;
SeekBar主要是监听器中三个方法的使用