09-HomeFragment中自定义圆形进度条ui-RoundProgress
/**
* @author: Hashub
* @WeChat: NGSHMVP
* @Date: 2018/9/19 21:30
* @function:自定义的圆形进度条
*/
public class RoundProgress extends View
{
//设置绘制的圆环及文本的属性---->使用自定义属性替换
// private int roundColor = Color.GRAY;//圆环的颜色
// private int roundProgressColor = Color.RED;//圆弧的颜色
// private int textColor = Color.BLUE;//文本的颜色
//
// private int roundWidth = UIUtils.dp2px(10);//圆环的宽度
// private int textSize = UIUtils.dp2px(20);//文本的字体大小
//
// private int max = 100;//圆环的最大值
// private int progress = 60;//圆环的进度
//使用自定义属性来初始化如下的变量
private int roundColor;//圆环的颜色
private int roundProgressColor;//圆弧的颜色
private int textColor;//文本的颜色
private float roundWidth;//圆环的宽度
private float textSize;//文本的字体大小
private int max;//圆环的最大值
private int progress;//圆环的进度
private int width;//当前视图的宽度(=高度)
private Paint paint;//画笔
public RoundProgress(Context context)
{
this(context, null);
}
public RoundProgress(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
}
public RoundProgress(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
//初始化画笔
paint = new Paint();
paint.setAntiAlias(true);//去除毛边
//获取自定义的属性
//1.获取TypeArray的对象(调用两个参数的方法)
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundProgress);
//2.取出所有的自定义属性
roundColor = typedArray.getColor(R.styleable.RoundProgress_roundColor, Color.GRAY);
roundProgressColor = typedArray.getColor(R.styleable.RoundProgress_roundProgressColor, Color.RED);
textColor = typedArray.getColor(R.styleable.RoundProgress_textColor, Color.GREEN);
roundWidth = typedArray.getDimension(R.styleable.RoundProgress_roundWith, UIUtils.dp2px(10));
textSize = typedArray.getDimension(R.styleable.RoundProgress_textSize, UIUtils.dp2px(20));
max = typedArray.getInteger(R.styleable.RoundProgress_max, 100);
progress = typedArray.getInteger(R.styleable.RoundProgress_progress, 30);
//3.回收处理
typedArray.recycle();
}
public int getMax()
{
return max;
}
public void setMax(int max)
{
this.max = max;
}
public int getProgress()
{
return progress;
}
public void setProgress(int progress)
{
this.progress = progress;
}
//测量:获取当前视图宽高
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = this.getMeasuredWidth();
}
/**
* canvas:画布,对应着视图在布局中的范围区间。范围的左上顶点即为坐标原点
*
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas)
{
//1.绘制圆环
//获取圆心坐标
int cx = width / 2;
int cy = width / 2;
float radius = width / 2 - roundWidth / 2;
paint.setColor(roundColor);//设置画笔颜色
paint.setStyle(Paint.Style.STROKE);//设置圆环的样式
paint.setStrokeWidth(roundWidth);//设置圆环的宽度
canvas.drawCircle(cx, cy, radius, paint);
//2.绘制圆弧
RectF rectF = new RectF(roundWidth / 2, roundWidth / 2, width - roundWidth / 2, width - roundWidth / 2);
paint.setColor(roundProgressColor);//设置画笔颜色
canvas.drawArc(rectF, 0, progress * 360 / max, false, paint);
//3.绘制文本
String text = progress * 100 / max + "%";
//设置paint
paint.setColor(textColor);
paint.setTextSize(textSize);
paint.setStrokeWidth(0);
Rect rect = new Rect();//创建了一个矩形,此时矩形没有具体的宽度和高度
paint.getTextBounds(text, 0, text.length(), rect);//此时的矩形的宽度和高度即为整好包裹文本的矩形的宽高
//获取左下顶点的坐标
int x = width / 2 - rect.width() / 2;
int y = width / 2 + rect.height() / 2;
canvas.drawText(text, x, y, paint);
}
}
010-HomeFragment中自定义ui-MyScrollView,实现头尾部的下拉、上拉
/**
* @author: Hashub
* @WeChat: NGSHMVP
* @Date: 2018/9/19 21:31
* @function:自定义尾部上拉下拉
*/
public class MyScrollView extends ScrollView
{
private View childView;
public MyScrollView(Context context)
{
super(context);
}
public MyScrollView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
}
//获取子视图
@Override
protected void onFinishInflate()
{
super.onFinishInflate();
if (getChildCount() > 0)
{
childView = getChildAt(0);
}
}
private int lastY;//上一次y轴方向操作的坐标位置
private Rect normal = new Rect();//用于记录临界状态的左、上、右、下
private boolean isFinishAnimation = true;//是否动画结束
private int lastX, downX, downY;
//拦截:实现父视图对子视图的拦截
//是否拦截成功,取决于方法的返回值。返回值true:拦截成功。反之,拦截失败
@Override
public boolean onInterceptTouchEvent(MotionEvent ev)
{
boolean isIntercept = false;
int eventX = (int) ev.getX();
int eventY = (int) ev.getY();
switch (ev.getAction())
{
case MotionEvent.ACTION_DOWN:
lastX = downX = eventX;
lastY = downY = eventY;
break;
case MotionEvent.ACTION_MOVE:
//获取水平和垂直方向的移动距离
int absX = Math.abs(eventX - downX);
int absY = Math.abs(eventY - downY);
if (absY > absX && absY >= UIUtils.dp2px(10))
{
isIntercept = true;//执行拦截
}
lastX = eventX;
lastY = eventY;
break;
}
return isIntercept;
}
@Override
public boolean onTouchEvent(MotionEvent ev)
{
if (childView == null || !isFinishAnimation)
{
return super.onTouchEvent(ev);
}
int eventY = (int) ev.getY();//获取当前的y轴坐标
switch (ev.getAction())
{
case MotionEvent.ACTION_DOWN:
lastY = eventY;
break;
case MotionEvent.ACTION_MOVE:
int dy = eventY - lastY;//微小的移动量
if (isNeedMove())
{
if (normal.isEmpty())
{
//记录了childView的临界状态的左、上、右、下
normal.set(childView.getLeft(), childView.getTop(), childView.getRight(), childView.getBottom());
}
//重新布局
childView.layout(childView.getLeft(), childView.getTop() + dy / 2, childView.getRight(), childView.getBottom() + dy / 2);
}
lastY = eventY;//重新赋值
break;
case MotionEvent.ACTION_UP:
if (isNeedAnimation())
{
//使用平移动画
int translateY = childView.getBottom() - normal.bottom;
TranslateAnimation translateAnimation = new TranslateAnimation(0, 0, 0, -translateY);
translateAnimation.setDuration(200);
// translateAnimation.setFillAfter(true);//停留在最终位置上
translateAnimation.setAnimationListener(new Animation.AnimationListener()
{
@Override
public void onAnimationStart(Animation animation)
{
isFinishAnimation = false;
}
@Override
public void onAnimationEnd(Animation animation)
{
isFinishAnimation = true;
childView.clearAnimation();//清除动画
//重新布局
childView.layout(normal.left, normal.top, normal.right, normal.bottom);
//清除normal的数据
normal.setEmpty();
}
@Override
public void onAnimationRepeat(Animation animation)
{
}
});
//启动动画
childView.startAnimation(translateAnimation);
}
break;
}
return super.onTouchEvent(ev);
}
//判断是否需要执行平移动画
private boolean isNeedAnimation()
{
return !normal.isEmpty();
}
private boolean isNeedMove()
{
int childMeasuredHeight = childView.getMeasuredHeight();//获取子视图的高度
int scrollViewMeasuredHeight = this.getMeasuredHeight();//获取布局的高度
Log.e("TAG", "childMeasuredHeight = " + childMeasuredHeight);
Log.e("TAG", "scrollViewMeasuredHeight = " + scrollViewMeasuredHeight);
int dy = childMeasuredHeight - scrollViewMeasuredHeight;//dy >= 0
int scrollY = this.getScrollY();//获取用户在y轴方向上的偏移量 (上 + 下 -)
if (scrollY <= 0 || scrollY >= dy)
{
return true;//按照我们自定义的MyScrollView的方式处理
}
//其他处在临界范围内的,返回false。即表示,仍按照ScrollView的方式处理
return false;
}
}
011-BaseFragment的抽取
public abstract class BaseFragment extends Fragment
{
private LoadingPage loadingPage;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
loadingPage = new LoadingPage(container.getContext())
{
@Override
public int layoutId()
{
return getLayoutId();
}
@Override
protected void onSuccss(ResultState resultState, View view_success)
{
ButterKnife.bind(BaseFragment.this, view_success);
initTitle();
initData(resultState.getContent());
}
@Override
protected RequestParams params()
{
return getParams();
}
@Override
public String url()
{
return getUrl();
}
};
return loadingPage;
}
/**
* 为了保证loadingPage不为null
* @param savedInstanceState
*/
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
getDataFromNet();
}
protected abstract RequestParams getParams();
protected abstract String getUrl();
//初始化界面的数据
protected abstract void initData(String content);
//初始化title
protected abstract void initTitle();
//提供布局
public abstract int getLayoutId();
@Override
public void onDestroyView()
{
super.onDestroyView();
ButterKnife.unbind(this);
}
public void getDataFromNet()
{
loadingPage.getDataFromNet();
}
}
012-LoadingPage的抽取
提供抽象的LoadingPage
/**
* @author: Hashub
* @WeChat: NGSHMVP
* @Date: 2018/9/19 18:51
* @function:根据实际联网获取数据情况加载不同的界面
*/
public abstract class LoadingPage extends FrameLayout
{
//1.定义4种不同的显示状态
private static final int STATE_LOADING = 1;
private static final int STATE_ERROR = 2;
private static final int STATE_EMPTY = 3;
private static final int STATE_SUCCESS = 4;
private int state_current = STATE_LOADING;//默认情况下,当前状态为正在加载
//2.提供4种不同的界面
private View view_loading;
private View view_error;
private View view_empty;
private View view_success;
private LayoutParams params;
private ResultState resultState;//状态枚举类对象
public LoadingPage(Context context)
{
this(context, null);
}
public LoadingPage(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
}
public LoadingPage(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
initLayout();//初始化布局
}
/**
* 初始化布局方法
*/
private void initLayout()
{
//实例化view
//1.提供布局显示的参数
params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
if (view_loading == null)
{
//2.加载布局
view_loading = UIUtils.getView(R.layout.page_loading);
//3.添加到当前的frameLayout中
addView(view_loading, params);//viewGroup中的addView()方法
}
if (view_empty == null)
{
//2.加载布局
view_empty = UIUtils.getView(R.layout.page_empty);
//3.添加到当前的frameLayout中
addView(view_empty, params);
}
if (view_error == null)
{
//2.加载布局
view_error = UIUtils.getView(R.layout.page_error);
//3.添加到当前的frameLayout中
addView(view_error, params);
}
showSafePage();
}
/**
* 保证如下的操作在主线程中执行的:更新界面
*/
private void showSafePage()
{
UIUtils.runOnUiThread(new Runnable()
{
@Override
public void run()
{
//保证run()中的操作在主线程中执行
showPage();
}
});
}
/**
* 四套界面实际上是叠加显示的,根据当前state_current的值,决定显示哪个view,隐藏其他view
*/
private void showPage()
{
view_loading.setVisibility(state_current == STATE_LOADING ? View.VISIBLE : View.INVISIBLE);
view_error.setVisibility(state_current == STATE_ERROR ? View.VISIBLE : View.INVISIBLE);
view_empty.setVisibility(state_current == STATE_EMPTY ? View.VISIBLE : View.INVISIBLE);
if (view_success == null)
{
view_success = UIUtils.getView(layoutId());
addView(view_success, params);
}
view_success.setVisibility(state_current == STATE_SUCCESS ? View.VISIBLE : View.INVISIBLE);
}
/**
* 在getDataFromNet()中实现联网加载数据
*/
public void getDataFromNet()
{
String url = url();
//如果页面不需要联网请求,即url为空,则成功显示本身页面
if (TextUtils.isEmpty(url))
{
resultState = ResultState.SUCCESS;
resultState.setContent("");
return;
}
//如果不为空,根据实际url联网请求数据
//模拟联网延迟2s效果
UIUtils.getHandler().postDelayed(new Runnable()
{
@Override
public void run()
{
AsyncHttpClient client = new AsyncHttpClient();
client.get(url(), params(), new AsyncHttpResponseHandler()
{
@Override
public void onSuccess(String content)
{
if (TextUtils.isEmpty(content))
{// "" or null
// state_current = STATE_EMPTY;
resultState = ResultState.EMPTY;
resultState.setContent("");
}
else
{
// state_current = STATE_SUCCESS;
resultState = ResultState.SUCCESS;
resultState.setContent(content);
}
// showSafePage();
loadPage();
}
@Override
public void onFailure(Throwable error, String content)
{
// state_current = STATE_ERROR;
resultState = ResultState.ERROR;
resultState.setContent("");
// showSafePage();
loadPage();
}
});
}
}, 2000);
}
private void loadPage()
{
switch (resultState)
{
case ERROR:
state_current = STATE_ERROR;
break;
case EMPTY:
state_current = STATE_EMPTY;
break;
case SUCCESS:
state_current = STATE_SUCCESS;
break;
}
//根据修改以后的state_current,更新视图的显示。
showSafePage();
if (state_current == STATE_SUCCESS)
{
onSuccss(resultState, view_success);
}
}
/**
* 抽象方法,当显示的是成功界面时,加载的是实际的fragment布局
*
* @return
*/
public abstract int layoutId();
/**
* 提供联网的请求参数
*
* @return
*/
protected abstract RequestParams params();
/**
* 提供联网的请求地址
*
* @return
*/
public abstract String url();
/**
* 成功显示的页面
* @param resultState
* @param view_success
*/
protected abstract void onSuccss(ResultState resultState, View view_success);
/**
* 提供枚举类,封装联网以后的状态值和数据
*/
public enum ResultState
{
//public static final ERROR=new ResultState(2);一样的部分省略,不同的抽取出来,用逗号隔开
ERROR(2), EMPTY(3), SUCCESS(4);
int state;
ResultState(int state)
{
this.state = state;
}
private String content;
public String getContent()
{
return content;
}
public void setContent(String content)
{
this.content = content;
}
}
}
013-投资模块布局fragment_invest.xml的设置
上面是:TabPageIndicator
下面是:ViewPager + Fragment(3个:ProductListFragment,ProductRecommondFragment,ProductHotFragment)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<include layout="@layout/common_title"></include>
<com.viewpagerindicator.TabPageIndicator
android:id="@+id/tabpage_invest"
android:layout_width="match_parent"
android:layout_height="wrap_content"></com.viewpagerindicator.TabPageIndicator>
<android.support.v4.view.ViewPager
android:id="@+id/vp_invest"
android:layout_width="match_parent"
android:layout_height="match_parent"></android.support.v4.view.ViewPager>
</LinearLayout>
014-ViewPager+FragmentPagerAdapter显示投资整体模块数据
/**
* @author: Hashub
* @WeChat: NGSHMVP
* @Date: 2018/9/15 14:39
* @function:投资理财fragment
*/
public class InvestFragment extends BaseFragment
{
@Bind(R.id.iv_title_back)
ImageView ivTitleBack;
@Bind(R.id.tv_title)
TextView tvTitle;
@Bind(R.id.iv_title_setting)
ImageView ivTitleSetting;
@Bind(R.id.tabpage_invest)
com.viewpagerindicator.TabPageIndicator tabpageInvest;
@Bind(R.id.vp_invest)
ViewPager vpInvest;
@Override
protected RequestParams getParams()
{
return null;
}
@Override
protected String getUrl()
{
return null;
}
@Override
protected void initData(String content)
{
//1.加载三个不同的Fragment:ProductListFragment,ProductRecommondFragment,ProductHotFragment.
initFragments();
//2.ViewPager设置三个Fragment的显示
MyAdapter adapter = new MyAdapter(getFragmentManager());
vpInvest.setAdapter(adapter);
//将TabPagerIndicator与ViewPager关联
tabpageInvest.setViewPager(vpInvest);
}
private List<Fragment> fragmentList = new ArrayList<>();
private void initFragments()
{
ProductListFragment productListFragment = new ProductListFragment();
ProductRecommondFragment productRecommondFragment = new ProductRecommondFragment();
ProductHotFragment productHotFragment = new ProductHotFragment();
//添加到集合中
fragmentList.add(productListFragment);
fragmentList.add(productRecommondFragment);
fragmentList.add(productHotFragment);
}
protected void initTitle()
{
ivTitleBack.setVisibility(View.GONE);
tvTitle.setText("投资");
ivTitleSetting.setVisibility(View.GONE);
}
@Override
public int getLayoutId()
{
return R.layout.fragment_invest;
}
/**
* 提供PagerAdapter的实现
* 如果ViewPager中加载的是Fragment,则提供的Adpater可以继承于具体的:FragmentStatePagerAdapter或FragmentPagerAdapter
* FragmentStatePagerAdapter:适用于ViewPager中加载的Fragment过多,会根据最近最少使用算法,实现内存中Fragment的清理,避免溢出
* FragmentPagerAdapter:适用于ViewPager中加载的Fragment不多时,系统不会清理已经加载的Fragment。
*/
class MyAdapter extends FragmentPagerAdapter
{
public MyAdapter(FragmentManager fm)
{
super(fm);
}
@Override
public Fragment getItem(int position)
{
return fragmentList.get(position);
}
@Override
public int getCount()
{
return fragmentList == null ? 0 : fragmentList.size();
}
//提供TabPageIndicator的显示内容
@Override
public CharSequence getPageTitle(int position)
{
//方式一:
// if(position == 0){
// return "全部理财";
// }else if(position == 1){
// return "推荐理财";
// }else if(position == 2){
// return "热门理财";
// }
//方式二:
return UIUtils.getStringArr(R.array.invest_tab)[position];
}
}
}
关于主题:
android:theme="@style/Theme.PageIndicatorDefaults"
创建相应的styles.xml中的主题,并在功能清单文件中使用
<!--自定义viewpagerIndicator样式-->
<style name="StyledIndicators" parent="@android:style/Theme.Light">
<item name="vpiTabPageIndicatorStyle">@style/CustomTabPageIndicator</item>
<item name="android:windowNoTitle">true</item>
</style>
<style name="CustomTabPageIndicator" parent="Widget.TabPageIndicator">
<item name="android:background">@drawable/tab_indicator</item>
<item name="android:textAppearance">@style/CustomTabPageIndicator.Text</item>
<item name="android:textSize">14sp</item>
<item name="android:dividerPadding">8dp</item>
<item name="android:showDividers">middle</item>
<item name="android:paddingLeft">10dp</item>
<item name="android:paddingRight">10dp</item>
<item name="android:fadingEdge">horizontal</item>
<item name="android:fadingEdgeLength">8dp</item>
</style>
<style name="CustomTabPageIndicator.Text" parent="android:TextAppearance.Medium">
<item name="android:typeface">monospace</item>
<item name="android:textColor">@drawable/selector_tabtext</item>
</style>
关于Context的实现类:Application和Activity的不同
①new AlertDialog.Builder(activity):只能使用activity
②在地图的集成中:只能使用application
③Toast.makeText(application/activity):都可以使用
④View.inflate(activity,..):二者都可,但建议使用activity
⑤new TextView(activity);二者都可,但建议使用activity.
⑥其他情况下,如果使用activity会造成内存泄漏的话,建议替换为application。
网友评论