文章目录
-
- 1.activity_main.xml
- 2.MianActivity
前几天一个朋友问我一下图片这个进度条怎么好实现
他说他的思路是progressBar+OnDraw方法绘制文字
当时看到这个图,我第一个想法就是可以通过TextView,然后通过线程去动态改变控件的宽度
他说没怎么明白,然后我自己就写了个demo,就顺便更到博客上
总的来说呐,实现思路其实还是蛮简单,首先定义好自己想要的样式的进度条然后获取屏幕的宽度,减去你设置的外边距,也就是获取你进度条的总长度,然后算出每一个进度你需要的前进的宽度是多少,(因为这里是整数,所以在进度条满了的时候还需要补齐误差值),所有dp的单位根据屏幕密度换算成px单位,然后再通过线程通信动态更新进度条的进度,就O了。
1.activity_main.xml
直接贴代码了
<RelativeLayout
android:layout_width="match_parent"
android:background="@color/colorWhite"
android:layout_height="wrap_content"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_ma_progress_backgroud"
android:layout_width="match_parent"
android:layout_height="35dp"
android:background="@drawable/progressbar_shape_gray"
android:layout_marginLeft="30dp"
android:layout_marginRight="30dp"
android:textColor="@color/colorWhite"
android:gravity="right"
android:layout_marginTop="30dp"
android:paddingRight="5dp"
android:textSize="14sp"
/>
<LinearLayout
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_ma_progress"
android:layout_width="1dp"
android:layout_height="35dp"
android:background="@drawable/progressbar_shape_orange"
android:textColor="@color/colorWhite"
android:gravity="right|center_vertical"
android:layout_marginTop="30dp"
android:paddingRight="5dp"
android:textSize="14sp"
/>
</LinearLayout>
</RelativeLayout>
这边之所以用个线性布局,是为了方便后期动态设置这个texview的layoutParams属性
2.MianActivity
public class MainActivity extends AppCompatActivity {
/**
* 进度条
*/
TextView tv_ma_progress;
/**
* 屏幕宽高
*/
public static int screenWidth;
/**
* 总进度值
*/
public int totalProgress = 100;
/**
* 起始进度值
*/
public int beginProgress = 0;
/**
* 平均宽度
*/
public int averageWidth = 1;
/**
* 因为求平均值的时候可能会除不尽 所以这里要求误差值
*/
public int errorValue = 0;
/**
* 外边距magin (dp)
*/
public int marginSize = 30;
/**
* 屏幕密度
*/
DisplayMetrics dm;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dm = new DisplayMetrics();
dm = getResources().getDisplayMetrics();
screenWidth = getWindowManager().getDefaultDisplay().getWidth();
tv_ma_progress = findViewById(R.id.tv_ma_progress);
averageWidth = (screenWidth-convertPx(marginSize)*2)/totalProgress;
errorValue = screenWidth-convertPx(marginSize)*2 - averageWidth*totalProgress;
new Thread(new Runnable() {
@Override
public void run() {
while (beginProgress<totalProgress){
beginProgress+=1;
try{
Thread.sleep(100);
}catch (Exception e){
e.printStackTrace();
}
Message message = new Message();
ProgressDto progressDto = new ProgressDto();
progressDto.setCurrentProgress(beginProgress);
int realShowWidth = averageWidth * beginProgress;
if(beginProgress>=totalProgress){
//因为之前求平均值时会有偏差,所以进度最后要加上误差值
realShowWidth = realShowWidth+errorValue;
}
progressDto.setCurrentWidth(realShowWidth);
message.obj = progressDto;
message.what = 1;
progressHandler.sendMessage(message);
}
}
}).start();
}
Handler progressHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 1:
ProgressDto progressDto = (ProgressDto) msg.obj;
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(progressDto.getCurrentWidth(), convertPx(35));
layoutParams.setMargins(convertPx(marginSize), convertPx(marginSize), convertPx(marginSize), convertPx(marginSize));
if(progressDto.getCurrentProgress()<=5){
tv_ma_progress.setTextSize(TypedValue.COMPLEX_UNIT_SP, 4);
}else if(progressDto.getCurrentProgress()<=10){
tv_ma_progress.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
}else{
tv_ma_progress.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
}
tv_ma_progress.setLayoutParams(layoutParams);
tv_ma_progress.setText("+"+progressDto.getCurrentProgress());
break;
}
}
};
class ProgressDto{
private int currentProgress;
private int currentWidth;
public int getCurrentProgress() {
return currentProgress;
}
public void setCurrentProgress(int currentProgress) {
this.currentProgress = currentProgress;
}
public int getCurrentWidth() {
return currentWidth;
}
public void setCurrentWidth(int currentWidth) {
this.currentWidth = currentWidth;
}
}
/**
* 根据dp转换成px
* px = dp*ppi/160
* dp = px / (ppi / 160)
* px = sp*ppi/160
*
* @return
*/
public int convertPx(int dp) {
return dp * dm.densityDpi / 160;
}
}
先贴上所有代码,再分析
这里主要是拿取屏幕宽度,和密度,拿密度其实主要是为了将dp转换成px,因为我们拿到的宽度是px为单位,我们动态设置控件高度宽度外边距之类的时候,其实都是px单位,但是我平时是用dp用习惯了,所以这边需要用到转换
dm = new DisplayMetrics();
dm = getResources().getDisplayMetrics();
screenWidth = getWindowManager().getDefaultDisplay().getWidth();
public int convertPx(int dp) {
return dp * dm.densityDpi / 160;
}
这句代码应该好理解,用屏幕的宽度减去两边设置的外边距再除以总进度,从而得到每一个进度所对应的宽度是多少
averageWidth = (screenWidth-convertPx(marginSize)*2)/totalProgress;
这句代码是在计算误差值,因为上一句求宽度平均值的时候,难免会有余数,所以这边暂且叫他误差值吧,进度满的时候,需要自动补齐
errorValue = screenWidth-convertPx(marginSize)*2 - averageWidth*totalProgress;
设置宽度的时候,我建议是直接用layoutParams,用以下两种形式设置宽度,不管用
tv_ma_progress.getLayoutParams().width;
tv_ma_progress.setWidth();
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(progressDto.getCurrentWidth(), convertPx(35));
layoutParams.setMargins(convertPx(marginSize), convertPx(marginSize), convertPx(marginSize), convertPx(marginSize));
这里其实就是动态设置字体大小的,本来没什么好说的,但是我之前踩过一个坑用的是tv_ma_progress.setTextSize()里面只有一个参数的,一个参数的确实会有问题,所以建议动态设置字体大小还是用下图这个
if(progressDto.getCurrentProgress()<=5){
tv_ma_progress.setTextSize(TypedValue.COMPLEX_UNIT_SP, 4);
}else if(progressDto.getCurrentProgress()<=10){
tv_ma_progress.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
}else{
tv_ma_progress.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
}
github地址:https://github.com/carriefeng/SelfProgressDemo