2、 好处与用法
使用DialogFragment来管理对话框,当旋转屏幕和按下后退键时可以更好的管理其声明周期,它和Fragment有着基本一致的声明周期。且DialogFragment也允许开发者把Dialog作为内嵌的组件进行重用,类似Fragment(可以在大屏幕和小屏幕显示出不同的效果)。
以上引用:http://blog.csdn.net/lmj623565791/article/details/37815413
闲话少说直接先上效果图,以防那么没耐心同学直接跑掉
非常简单中间就一个动画。
自定义的loadingView 效果如下
public class LoadingView extends View implements ValueAnimator.AnimatorUpdateListener { private Context context; private Paint paint; private PathMeasure pathMeasure; private Path showPath; private int mHeight; private int mWith; private ValueAnimator valueAnimator; private float dtPath; private float startPoint; private int radius = 80; public LoadingView(Context context) { super(context); init(context); } public LoadingView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public LoadingView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { this.context = context; paint = new Paint(); paint.setAntiAlias(true); paint.setColor(Color.RED); paint.setStrokeWidth(Displayutil.dip2px(getContext(), 3)); paint.setStyle(Paint.Style.STROKE); Path path = new Path(); path.addCircle(0, 0, radius, Path.Direction.CW); pathMeasure = new PathMeasure(path, false); valueAnimator = ValueAnimator.ofFloat(0, ((float) (2 * radius * Math.PI) / 4)); valueAnimator.setDuration(2000).setRepeatCount(Animation.INFINITE); valueAnimator.addUpdateListener(this); valueAnimator.start(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); // 在wrap_content的情况下默认长度为200dp int minSize = Displayutil.dip2px(context, radius); // wrap_content的specMode是AT_MOST模式,这种情况下宽/高等同于specSize // 查表得这种情况下specSize等同于parentSize,也就是父容器当前剩余的大小 // 在wrap_content的情况下如果特殊处理,效果等同martch_parent if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(minSize, minSize); } else if (widthSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(minSize, heightSpecSize); } else if (heightSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(widthSpecSize, minSize); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mHeight = getMeasuredHeight(); mWith = getMeasuredWidth(); canvas.translate(mWith / 2, getHeight() / 2); Path path = new Path(); pathMeasure.getSegment(startPoint, startPoint , path, true); if (startPoint + dtPath < 2 * radius * Math.PI) { pathMeasure.getSegment(startPoint, startPoint + dtPath, path, true); } else { pathMeasure.getSegment(startPoint, (float) (2 * radius * Math.PI), path, true); } canvas.drawPath(path, paint); } @Override public void onAnimationUpdate(ValueAnimator animation) { dtPath = (float) animation.getAnimatedValue(); startPoint = dtPath * ((float) (2 * radius * Math.PI)) / ((float) (2 * radius * Math.PI) / 4); if(startPoint!=0){ postInvalidateDelayed(300); } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); valueAnimator.end(); } }下面一步步说咯
1继承view 处理onMeasure,
这里不多说,下面这些代码,就是为了让warp_content 写法固定,注释写的很详细了,不理解的建议,看viewmeasure过程源码,不细说,如果需要,后面我会写一篇博客继续说。
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); // 在wrap_content的情况下默认长度为200dp int minSize = Displayutil.dip2px(context, radius); // wrap_content的specMode是AT_MOST模式,这种情况下宽/高等同于specSize // 查表得这种情况下specSize等同于parentSize,也就是父容器当前剩余的大小 // 在wrap_content的情况下如果特殊处理,效果等同martch_parent if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(minSize, minSize); } else if (widthSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(minSize, heightSpecSize); } else if (heightSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(widthSpecSize, minSize); }
2 看init方法
private void init(Context context) { this.context = context; paint = new Paint(); paint.setAntiAlias(true); paint.setColor(Color.RED); paint.setStrokeWidth(Displayutil.dip2px(getContext(), 3)); paint.setStyle(Paint.Style.STROKE); Path path = new Path(); path.addCircle(0, 0, radius, Path.Direction.CW); pathMeasure = new PathMeasure(path, false); valueAnimator = ValueAnimator.ofFloat(0, ((float) (2 * radius * Math.PI) / 4)); valueAnimator.setDuration(2000).setRepeatCount(Animation.INFINITE); valueAnimator.addUpdateListener(this); valueAnimator.start(); }init 做了3件事情:
1 初始化了画笔
2规划好了,我们要取的path 路径,就是一个环圈,我们后面取的路径都是从这个圆圈中取的。
Path path = new Path(); path.addCircle(0, 0, radius, Path.Direction.CW);
3 初始话了一个属性动画,设置了监听。
监听内的方法。
@Override public void onAnimationUpdate(ValueAnimator animation) { dtPath = (float) animation.getAnimatedValue(); startPoint = dtPath * ((float) (2 * radius * Math.PI)) / ((float) (2 * radius * Math.PI) / 4); if(startPoint!=0){ postInvalidateDelayed(300); } }dapath 就是我们需要绘制圆弧的长度,通过看valueanimatior 知道最小是0,最大是圆的周长的1/4.
startPoint 开始绘制圆弧的位置,原理很简单,根据dapth 实际值于最大值的比例,确定开始绘制的位置,保证在dapth 长度变化一个周期内,开始绘制位置沿着圆周移动一圈。
然后invalidateDekayed 引起view从新绘制,
处理ondraw()这里就是动画的重点了,这里实现方式配合了valueAnimator ,你也可以自定义插值起根估值器来实现,那样稍微复杂,这里直接使用了动画,但是自定义控件时候,使用动画,小心内存泄漏问题。后面我们会说。下面一行一行的说。
mHeight = getMeasuredHeight();
mWith = getMeasuredWidth(); 获取到空间的宽高,注意不要直接使用getheight() getWidth(), 可能会得不到宽高,这也是view 测试基础了,不多说。
canvas.translate(mWith / 2, getHeight() / 2);
Path path = new Path();
pathMeasure.getSegment(startPoint, startPoint + dtPath, path, true);
这是最关键的一个方法了,4个参数分别代表,开始截取path的起点,path截取终点,path 用来保存我们要截取的对象,最后一个参数表示截取的路径是狗需要首位相连。
if (startPoint + dtPath < 2 * radius * Math.PI) {
pathMeasure.getSegment(startPoint, startPoint + dtPath, path, true);
} else {
pathMeasure.getSegment(startPoint, (float) (2 * radius * Math.PI), path, true);
}
绘制
canvas.drawPath(path, paint);
@Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); valueAnimator.end(); }解决使用动画可能引起的内存泄漏问题。当view离开window 时候将动画关闭。
dialogFragment 简单许多了
public class LoadingDialog extends DialogFragment{ @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE); getDialog().setCanceledOnTouchOutside(false); getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); return inflater.inflate(R.layout.loding_dialog,null); } }去掉了标题,使用style 将背景变为透明的,设置了dialog 的布局,
注意使用dailogfragment 时候oncreateview 与onCreateDIalog方法只能实现一个,否则的话会报错,上面鸿洋大神博客说的很清楚是或者关系。
好了这样一个简单的dialogfragment 就完成了。