大概页面是这样子,因为目前项目没上线,我也不能把图露出来。
my package上面的是固定的,是header,下面的package是获取到的信息需要展示的,也就是item
WIFI 和MEDIA 中间的两个圆圈是一个自定义view,一个圆圈加载的东西,通过获取数据并且画出来。
fragment_data_flow
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="620dp"
android:layout_height="300dp"
android:layout_marginLeft="250dp"
android:layout_marginTop="70dp"
android:orientation="vertical"
tools:context=".mainPage.DataFlowFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="horizontal">
<ImageView
android:layout_width="40dp"
android:layout_height="35dp"
android:layout_gravity="center_vertical"
android:id="@+id/back_main"
android:src="@drawable/ic_part_account_back"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/data_flow"
android:textSize="20dp"/>
<TextView
android:layout_width="100dp"
android:layout_height="35dp"
android:layout_gravity="center_vertical"
android:id="@+id/buy"
android:layout_marginLeft="350dp"
android:text="@string/buy"
android:textSize="20dp"
android:gravity="center"
android:background="@drawable/part_account_buy"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="10dp">
<!-- 左边wifi的部分-->
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/wifi_fl"/>
<!-- 右边media的部分-->
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:id="@+id/media_fl"/>
</LinearLayout>
</LinearLayout>
这是DataFlowFragment的xml部分。两个frameLayout分别为两部分fragment
package com.xxx.personalcenter.mainPage;
import android.os.Build;
import android.view.View;
import android.widget.ImageView;
import androidx.annotation.RequiresApi;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import com.xxx.personalcenter.R;
import com.xxx.personalcenter.base.BaseFragment;
public class DataFlowFragment extends BaseFragment implements View.OnClickListener{
private static final String TAG = "DataFlowFragment";
private ImageView backBtn;
// 获取实例
public static DataFlowFragment getInstance() {
return DataFlowFragment.DataFlowHolder.get();
}
// 这个页面可以做成 如果流量页面加载失败,显示这个页面。可以点击按钮重新加载
@Override
protected int getLayoutId() {
return R.layout.fragment_data_flow;
}
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
protected void initView(View rootView) {
backBtn=rootView.findViewById(R.id.back_main);
// 分别加载两部分fragment
replace(FlowMediaFragment.getInstance(),"mediaFragment",R.id.media_fl);
replace(FlowWifiFragment.getInstance(),"wifiFragment",R.id.wifi_fl);
}
@Override
protected void initListener(){
backBtn.setOnClickListener(this);
}
@Override
protected void loadData() {
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.back_main:
FragmentManager manager= requireActivity().getSupportFragmentManager();
manager.popBackStack("personal_center",0);
break;
default:
break;
}
}
private static class DataFlowHolder {
private static final DataFlowFragment INSTANCE = new DataFlowFragment();
public static DataFlowFragment get() {
return INSTANCE;
}
}
public void replace(Fragment fragment, String str,int page) {
FragmentManager manager = requireActivity().getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(page, fragment);
transaction.addToBackStack(str);
transaction.commit();
}
}
package com.xxx.personalcenter.base;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import org.jetbrains.annotations.NotNull;
public abstract class BaseFragment extends Fragment {
@Nullable
@org.jetbrains.annotations.Nullable
@Override
public View onCreateView(@NonNull @NotNull LayoutInflater inflater, @Nullable @org.jetbrains.annotations.Nullable ViewGroup container, @Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
View rootView=inflater.inflate(getLayoutId(),container,false);
initView(rootView);
initListener();
loadData();
return rootView;
}
protected abstract int getLayoutId();
protected abstract void initView(View rootView);
protected abstract void initListener();
protected abstract void loadData();
}
这部分是我项目中BaseFragment部分。所有的Fragment都继承它
FlowMediaFragment 部分代码
package com.xxx.personalcenter.mainPage;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.lifecycle.Observer;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.xxx.tools.https.ApiFactory;
import com.xxx.tools.https.Resp;
import com.xxx.personalcenter.R;
import com.xxx.personalcenter.adapter.FlowPackageAdapter;
import com.xxx.personalcenter.base.BaseFragment;
import com.xxx.personalcenter.unio.Constants;
import com.xxx.personalcenter.unio.FlowList;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class FlowMediaFragment extends BaseFragment implements SwipeRefreshLayout.OnRefreshListener{
private static final String TAG = "FlowMediaFragment";
private RecyclerView media_rv;
private FlowPackageAdapter adapterMedia;
// 下面这几个list是我处理获取得到的数据
// 用于接受请求中返回的media和wifi集合
private List<FlowList.FlowDetail> flowList;
// 用于存放过滤之后的media package
private List<FlowList.FlowDetail> mediaListFilter =new ArrayList<>();
// 用于存放每次往adapter的list中设置的media package
private List<FlowList.FlowDetail> mediaList=new ArrayList<>();
private SwipeRefreshLayout swipeMedia;
// media剩余长度
private int mediaSurLength;
private int mediaI=0;
public static FlowMediaFragment getInstance() {
return FlowMediaFragment.FlowMediaHolder.get();
}
@Override
protected int getLayoutId() {
return R.layout.fragment_media_flow;
}
@Override
protected void initView(View rootView) {
media_rv=rootView.findViewById(R.id.media_rv_view);
swipeMedia =rootView.findViewById(R.id.media_sr_refresh);
initAdapter();
}
@Override
protected void initListener() {
swipeMedia.setOnRefreshListener(this);
}
@Override
protected void loadData() {
getFlowList();
}
private void loadMore(){
LinearLayoutManager manager=new LinearLayoutManager(getContext());
media_rv.setLayoutManager(manager);
adapterMedia=new FlowPackageAdapter(mediaList,getContext(),Constants.mediaPercentage,Constants.TYPE_MEDIA);
media_rv.setItemAnimator(new DefaultItemAnimator());
media_rv.setAdapter(adapterMedia);
Log.d(TAG, "loadMore: media="+Constants.wifiPercentage);
Log.d(TAG, "loadMore: mediaList="+ mediaListFilter.toString());
}
// load media
private void loadMedia(){
Log.d(TAG, "loadMedia:");
if (mediaSurLength >2 ){
for (int i=0;i<2;i++){
mediaList.add(mediaListFilter.get(mediaI));
mediaSurLength--;
mediaI++;
Log.d(TAG, "loadMedia: ");
}
Log.d(TAG, "loadMedia: mediaSurLength="+mediaSurLength);
adapterMedia.hasMore(1);
}else if (mediaSurLength >0){
for (; mediaI< mediaListFilter.size(); mediaI++){
mediaList.add(mediaListFilter.get(mediaI));
mediaSurLength--;
Log.d(TAG, "loadMedia: ");
}
adapterMedia.hasMore(1);
}else {
Log.d(TAG, "loadMedia: ");
adapterMedia.hasMore(2);
}
adapterMedia.notifyDataSetChanged();
adapterMedia.hasMore(3);
}
private void initAdapter(){
loadMore();
media_rv.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull @NotNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
/**
* 每次去通知adapter去更新数据的时候,先去开启加载动画,动画加载完之后再去通知关闭动画(loadMedia中的 hasMore(3) 表示关闭动画) 每次在加载数据的时候,要先开启加载动画,等数据加载进来之后在关闭加载动画
*/
adapterMedia.hasMore(1);
adapterMedia.notifyDataSetChanged();
if (newState==RecyclerView.SCROLL_STATE_IDLE){
//recyclerview滑动到底部,更新数据
//加载更多数据
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
if (mediaSurLength > 0) {
loadMedia();
Log.d(TAG, "onScrollStateChanged: "+"开始加载数据");
} else {
//没有数据了
Log.d(TAG, "run: ");
adapterMedia.hasMore(2);
}
adapterMedia.notifyDataSetChanged();
swipeMedia.setRefreshing(false);
}
},1000);
}else if (newState==RecyclerView.INVALID_TYPE){
Log.d(TAG, "onScrollStateChanged: "+"media_INVALID_TYPE");
adapterMedia.hasMore(2);
}
}
});
}
// 这个是我将获取的数据进行处理的方法,在此就不展示了
private void filter(){//filter-
}
// 这是通过网络接口获取数据的方法,在此就不展示了
private void getFlowList(){
}
@Override
public void onRefresh() {
swipeMedia.setRefreshing(true);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//清空数据
adapterMedia.reData();
mediaI=0;
mediaSurLength =0;
//重新获取数据
getFlowList();
//更新的数据
//重新设置有更多数据
adapterMedia.hasMore(1);
adapterMedia.notifyDataSetChanged();
//隐藏刷新效果 感觉这个隐藏刷新效果只能隐藏下拉刷新时,上面那个圈,自己定义的footer效果不能隐藏
swipeMedia.setRefreshing(false);
adapterMedia.hasMore(1);
}
}, 1000);
}
private static class FlowMediaHolder {
private static final FlowMediaFragment INSTANCE = new FlowMediaFragment();
public static FlowMediaFragment get() {
return INSTANCE;
}
}
}
另一部分的wifi和media相同就不展示了。
FlowPackageAdapter
package com.xxx.personalcenter.adapter;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.xxx.personalcenter.R;
import com.xxx.personalcenter.unio.Constants;
import com.xxx.personalcenter.unio.FlowList;
import com.xxx.personalcenter.view.CirclePercentBar;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class FlowPackageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
// 用来区分时media还是wifi的类型
public static final int DATA_TYPE_WIFI =2;
public static final int DATA_TYPE_MEDIA =1;
private List<FlowList.FlowDetail> objects;
private static final String TAG = "FlowPackageAdapter";
private int TYPE_HEADER=0;
private int TYPE_ITEM = 1;//正常的Item
private int TYPE_FOOT = 2;//尾部刷新
// header 与 footer的个数
private int mHeaderCount=1;
private int mFooterCount=1;
private float mPercentage;
private Context mContext;
private int state=1;
private View mHeader;
private int dateType;
public FlowPackageAdapter(List<FlowList.FlowDetail> objects, Context mContext,float percentage,int dateType) {
this.objects = objects;
this.mContext = mContext;
this.mPercentage=percentage;
this.dateType=dateType;
}
@Override
public int getItemViewType(int position) {
int dataItemCount=getCountItemCount();
// 头部view
if (mHeaderCount!=0 && position<mHeaderCount){
return TYPE_HEADER;
}else if (mFooterCount!=0 && position>=(mHeaderCount+dataItemCount)){
return TYPE_FOOT;
}else {
return TYPE_ITEM;
}
}
public int getCountItemCount(){
return objects.size();
}
@NonNull
@NotNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull @NotNull ViewGroup parent, int viewType) {
// 根据不同的view加载不同的布局
if (viewType==TYPE_HEADER){
return new FlowPackageAdapter.HeaderHolder(LayoutInflater.from(mContext).inflate(R.layout.wifi_header_view,parent,false));
}else if (viewType==TYPE_ITEM){
return new FlowPackageAdapter.ItemHolder(LayoutInflater.from(mContext).inflate(R.layout.flow_my_package_item,parent,false));
}else {
return new FlowPackageAdapter.FootHolder(LayoutInflater.from(mContext).inflate(R.layout.foot_boot, parent, false));
}
}
@SuppressLint("SetTextI18n")
@Override
public void onBindViewHolder(@NonNull @NotNull RecyclerView.ViewHolder holder, int position) {
if (holder instanceof FlowPackageAdapter.ItemHolder) {
ItemHolder _holder=(ItemHolder) holder;
_holder.remain.setText(objects.get(position-mHeaderCount).getRemainValue()+"");
_holder.all.setText(objects.get(position-mHeaderCount).getTotalValue()+"");
_holder.packageName.setText(objects.get(position-mHeaderCount).getPackageName()+"");
} else if (holder instanceof FlowPackageAdapter.FootHolder){
Log.d(TAG, "onBindViewHolder: "+"footer");
FootHolder _holder=(FootHolder)holder;
switch (state){
case 1:
_holder.tv_foot.setText("Loading。。");
_holder.tv_foot.setVisibility(View.VISIBLE);
_holder.pb_foot.setVisibility(View.VISIBLE);
break;
case 2:
_holder.tv_foot.setText("No more");
_holder.tv_foot.setVisibility(View.VISIBLE);
_holder.pb_foot.setVisibility(View.GONE);
break;
case 3:
if (objects.size()!=0){
_holder.tv_foot.setVisibility(View.GONE);
}else {
_holder.tv_foot.setText("No more");
_holder.tv_foot.setVisibility(View.VISIBLE);
}
_holder.pb_foot.setVisibility(View.GONE);
break;
default:
break;
}
}else if (holder instanceof FlowPackageAdapter.HeaderHolder){
HeaderHolder _holder=(HeaderHolder)holder;
_holder.bar.setPercentData(mPercentage);
if(this.dateType== DATA_TYPE_WIFI){//left
_holder.flowType.setText(R.string.media_inner);
_holder.flowDescription.setText(R.string.media_right);
_holder.company.setText(Constants.wifiCompany+"");
_holder.bar.setRemainFlow(Constants.wifiRemain);
}else {//right
_holder.flowType.setText(R.string.wlan_inner);
_holder.flowDescription.setText(R.string.wlan_right);
_holder.company.setText(Constants.mediaCompany+"");
_holder.bar.setRemainFlow(Constants.mediaRemain);
}
}
}
@Override
public int getItemCount() {
return mHeaderCount+objects.size()+mFooterCount;
}
//item的holder
static class ItemHolder extends RecyclerView.ViewHolder {
TextView packageName;
TextView remain;
TextView all;
public ItemHolder(View itemView) {
super(itemView);
packageName=itemView.findViewById(R.id.packageName);
remain=itemView.findViewById(R.id.remain);
all=itemView.findViewById(R.id.all);
}
}
static class FootHolder extends RecyclerView.ViewHolder {
private TextView tv_foot;
private ProgressBar pb_foot;
public FootHolder(View itemView) {
super(itemView);
tv_foot = itemView.findViewById(R.id.tv_foot);
pb_foot = itemView.findViewById(R.id.pb_foot);
}
}
static class HeaderHolder extends RecyclerView.ViewHolder {
// 这个是自定义的控件
private CirclePercentBar bar;
private TextView flowType;
private TextView flowDescription;
private TextView company;
public HeaderHolder(View itemView) {
super(itemView);
company=itemView.findViewById(R.id.company);
bar=itemView.findViewById(R.id.circlePercentViewWifi);
flowType=itemView.findViewById(R.id.flowType);
flowDescription=itemView.findViewById(R.id.flowDescription);
}
}
/**
* 三种状态 1 还有更多数据
* 2 无更多数据
* 3 有更多数据,但是footer不需要再显示了
* @param
*/
public void hasMore(int state) {
Log.d(TAG, "hasMore: ="+state);
this.state = state;
}
//这里清空数据,避免下拉刷新后,还显示上拉加载的数据
public void reData(){
objects.clear();
}
}
wifi_header_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="300dp"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
tools:context=".mainPage.DataFlowFragment">
<LinearLayout
android:layout_width="300dp"
android:layout_height="wrap_content"
android:background="@drawable/home_page_round"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/media_inner"
android:textSize="15dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="20dp"
android:id="@+id/flowType"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/media_right"
android:textSize="10dp"
android:id="@+id/flowDescription"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10px"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginLeft="110dp"
android:layout_marginTop="38dp"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/available_flow"
android:text="@string/available_flow"
android:textSize="10dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/company"
android:text="(GB)"
android:textSize="12dp"
/>
</LinearLayout>
<com.xxx.personalcenter.view.CirclePercentBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
app:arcColor="#CCCCCC"
app:arcWidth="14dp"
app:centerTextColor="#E8E8E7"
app:centerTextSize="25sp"
app:circleRadius="65dp"
app:arcStartColor="#6A6969"
android:id="@+id/circlePercentViewWifi"/>
</FrameLayout>
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/my_package"
android:layout_marginLeft="20dp"
android:layout_marginBottom="5dp"/>
</LinearLayout>
</LinearLayout>
footer
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="280dp"
android:layout_height="40dp"
android:gravity="center"
android:orientation="horizontal"
android:background="@drawable/part_account_item_round">
<ProgressBar
android:layout_width="30dp"
android:layout_height="45dp"
android:id="@+id/pb_foot"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tv_foot"
android:visibility="visible"
android:textSize="10dp"/>
</LinearLayout>
item
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="280dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginLeft="8dp"
android:background="@drawable/part_account_item_round">
<LinearLayout
android:layout_width="280dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="center_horizontal"
android:layout_marginTop="5dp"
android:id="@+id/tipsTop"
android:background="@drawable/part_account_mypackage">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/data_flow_monthly"
android:layout_gravity="center_vertical"
android:id="@+id/packageName"
android:layout_marginLeft="8dp"
android:textSize="12dp"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="80dp"
android:layout_gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/remain"
android:text="0.0"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="/"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/all"
android:text="8.0 GB"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
自定义控件内容
package com.xxx.personalcenter.view;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.SweepGradient;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import com.xxx.personalcenter.R;
import com.xxx.personalcenter.util.DensityUtils;
import java.text.DecimalFormat;
import static android.content.ContentValues.TAG;
public class CirclePercentBar extends View {
private int mArcColor;
private int mArcWidth;
private int mCenterTextColor;
private int mCenterTextSize;
private int mCenterTextTopSize;
private int mCircleRadius;
private int mArcStarColor;
private float mRemainFlow;
private Paint startCirclePaint;
private Paint arcCirclePaint;
private Paint arcPaint;
private Paint centerTextPaint;
private Paint centerTextTopPaint;
private RectF arcRectF;
private Rect textBoundRect;
private float mCurData=0.0f;
public CirclePercentBar(Context context) {
this(context,null);
}
public CirclePercentBar(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public CirclePercentBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
Log.d(TAG, "CirclePercentBar:");
TypedArray typedArray=context.obtainStyledAttributes(attrs, R.styleable.CirclePercentBar,defStyleAttr,0);
mArcColor=typedArray.getColor(R.styleable.CirclePercentBar_arcColor,0xff0000);
mArcWidth=typedArray.getDimensionPixelSize(R.styleable.CirclePercentBar_arcWidth, DensityUtils.dp2px(context,20));
mCenterTextColor=typedArray.getColor(R.styleable.CirclePercentBar_arcColor,0x0000ff);
Log.d(TAG, "mArcWidth: "+mArcWidth);
mCenterTextSize=typedArray.getDimensionPixelSize(R.styleable.CirclePercentBar_centerTextSize,DensityUtils.dp2px(context,20));
mCenterTextTopSize=typedArray.getDimensionPixelSize(R.styleable.CirclePercentBar_centerTextTopSize,DensityUtils.dp2px(context,10));
mCircleRadius=typedArray.getDimensionPixelSize(R.styleable.CirclePercentBar_circleRadius,DensityUtils.dp2px(context,100));
mArcStarColor=typedArray.getColor(R.styleable.CirclePercentBar_arcStartColor, ContextCompat.getColor(getContext(),R.color.colorStart));
typedArray.recycle();
initPaint();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(measureView(widthMeasureSpec),measureView(heightMeasureSpec));
}
private int measureView(int measureSpec){
int result;
int specMod=MeasureSpec.getMode(measureSpec);
int specSize=MeasureSpec.getSize(measureSpec);
if (specMod==MeasureSpec.EXACTLY){
result=specSize;
}else {
result=mCircleRadius*2;
if (specMod==MeasureSpec.AT_MOST){
result=Math.min(result,specSize);
}
}
return result;
}
// 设置圆环的填充
public void setPercentData(float data){
// 处理浮点型数据。还有 ofInt..等等
ValueAnimator valueAnimator=ValueAnimator.ofFloat(mCurData,data);
valueAnimator.setDuration((long)(Math.abs(mCurData-data)*20));
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
// 这个value就是前面传过来的数据,在前面就应该换算成百分比数传过来。
float value=(float)valueAnimator.getAnimatedValue();
mCurData=(float) value;
invalidate();
}
});
valueAnimator.start();
}
// 设置圆环里字体的大小
public void setRemainFlow(float remain){
ValueAnimator valueAnimator=ValueAnimator.ofFloat(mRemainFlow,remain);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mRemainFlow= (float)valueAnimator.getAnimatedValue();
invalidate();
}
});
valueAnimator.start();
}
private void initPaint(){
Log.d(TAG, "initPaint:");
// 未填充状态的背景圆环
arcCirclePaint=new Paint(Paint.ANTI_ALIAS_FLAG);
arcCirclePaint.setStyle(Paint.Style.STROKE);
arcCirclePaint.setStrokeWidth(mArcWidth);
arcCirclePaint.setColor(ContextCompat.getColor(getContext(),R.color.circlePaint));
arcCirclePaint.setAlpha(30);
arcCirclePaint.setStrokeCap(Paint.Cap.BUTT);
// 百分比占据的圆弧
arcPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
// 只设置了描边
arcPaint.setStyle(Paint.Style.STROKE);
arcPaint.setStrokeWidth(mArcWidth);
arcPaint.setColor(mArcColor);
arcPaint.setStrokeCap(Paint.Cap.BUTT);
// 圆环中的百分比文字
centerTextPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
centerTextPaint.setStyle(Paint.Style.FILL);
centerTextPaint.setColor(mCenterTextColor);
centerTextPaint.setTextSize(mCenterTextSize);
// 圆环中百分比文字上面的文字
centerTextTopPaint=new Paint();
centerTextTopPaint.setStyle(Paint.Style.STROKE);
centerTextTopPaint.setColor(mCenterTextColor);
centerTextTopPaint.setTextSize(mCenterTextTopSize);
// 圆弧的外接矩形
arcRectF=new RectF();
// 文字的边界矩形
textBoundRect=new Rect();
}
@Override
protected void onDraw(Canvas canvas) {
Log.d(TAG, "onDraw: onDraw");
canvas.rotate(-90,getWidth()/2,getHeight()/2);
arcRectF.set(getWidth()/2-mCircleRadius+mArcWidth/2,getHeight()/2-mCircleRadius+mArcWidth/2,
getWidth()/2+mCircleRadius-mArcWidth/2,getHeight()/2+mCircleRadius-mArcWidth/2);
canvas.drawArc(arcRectF,0,360,false,arcCirclePaint);
// SweepGradient 扫描渐变
arcPaint.setShader(new SweepGradient(getWidth()/2,getHeight()/2,mArcStarColor,mArcStarColor));
// 绘制圆圈中间的文字
canvas.drawArc(arcRectF,0,360*mCurData/100,false,arcPaint);
canvas.rotate(90,getWidth()/2,getHeight()/2);
// canvas.drawCircle(getWidth()/2,getHeight()/2-mCircleRadius+mArcWidth/2,mArcWidth/2,startCirclePaint);
// 圆圈中间显示的文字
@SuppressLint("DrawAllocation") DecimalFormat format=new DecimalFormat("##0.00");
String data=format.format(mRemainFlow);
centerTextPaint.getTextBounds(data,0,data.length(),textBoundRect);
Log.d(TAG, "onDraw: getHeight="+getHeight());
Log.d(TAG, "onDraw: getWeight="+getWidth());
Log.d(TAG, "onDraw: gextBoundRect.height="+textBoundRect.height());
canvas.drawText(data,getWidth()/2-textBoundRect.width()/2,getHeight()/2+textBoundRect.height()/2,centerTextPaint);
}
}
自定义圆环中定义的一些属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CirclePercentBar">
<attr name="arcColor" format="color"/>
<attr name="arcWidth" format="dimension"/>
<attr name="centerTextColor" format="color"/>
<attr name="centerTextSize" format="dimension"/>
<attr name="centerTextTopSize" format="dimension"/>
<attr name="circleRadius" format="dimension"/>
<attr name="arcStartColor" format="color"/>
</declare-styleable>
</resources>