1.
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.net.Uri;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ListAdapter;
import android.widget.ListView;
public class MeasureUtil {
/**
* 应用程序App区域宽高等尺寸获取,最好在Activity的onWindowFocusChanged ()方法或者之后调运
*/
public static Rect getAppAreaRect(Activity context) {
Rect rect = new Rect();
context.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
return rect;
}
/**
* 获取状态栏高度,最好在Activity的onWindowFocusChanged ()方法或者之后调运
*/
public static int getStatusBarHeight(Activity context) {
Rect rect = new Rect();
context.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
return rect.top;
}
/**
* View布局区域宽高等尺寸获取,最好在Activity的onWindowFocusChanged ()方法或者之后调运
*/
public static Rect getContentViewRect(Activity context) {
Rect rect = new Rect();
context.getWindow().findViewById(Window.ID_ANDROID_CONTENT).getDrawingRect(rect);
return rect;
}
/**
* 获取状态栏的高度
*
* @param context 上下文
* @return 状态栏高度
*/
public static int getStatusBarHeight(Context context) {
int result = 0;
int resourceId = context.getResources().getIdentifier(
"status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
public static int getToolbarHeight(Context context) {
final TypedArray styledAttributes = context.getTheme().obtainStyledAttributes(
new int[]{R.attr.actionBarSize});
int toolbarHeight = (int) styledAttributes.getDimension(0, 0);
styledAttributes.recycle();
return toolbarHeight;
}
/**
* 可将当前view保存为图片的工具
*
* @param v
* @return
*/
public static Bitmap createViewBitmap(View v) {
Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
v.draw(canvas);
return bitmap;
}
/**
* 可将当前view保存为图片的工具
*
* @param view
* @return
*/
public static Bitmap convertViewToBitmap(View view) {
view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
view.buildDrawingCache();
Bitmap bitmap = view.getDrawingCache();
return bitmap;
}
/**
* 获取app内部资源的uri,用于fresco设置本地图片
*
* @param resId
* @param packageName
* @return
*/
public static Uri getResourceUri(int resId, String packageName) {
return Uri.parse("res://" + packageName + "/" + resId);
}
/**
* 获取屏幕尺寸
*
* @param activity Activity
* @return 屏幕尺寸像素值,下标为0的值为宽,下标为1的值为高
*/
public static int[] getScreenSize(Activity activity) {
DisplayMetrics metrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
return new int[]{metrics.widthPixels, metrics.heightPixels};
}
public static int getScreenWidth(Activity activity) {
DisplayMetrics metrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
return metrics.widthPixels;
}
public static int getScreenHeight(Activity activity) {
DisplayMetrics metrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
return metrics.heightPixels;
}
/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
/**
* 将px值转换为sp值,保证文字大小不变
*
* @param pxValue
* @param context (DisplayMetrics类中属性scaledDensity)
* @return
*/
public static int px2sp(Context context, float pxValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (pxValue / fontScale + 0.5f);
}
/**
* 将sp值转换为px值,保证文字大小不变
*
* @param spValue
* @param context (DisplayMetrics类中属性scaledDensity)
* @return
*/
public static int sp2px(Context context, float spValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
/**
* 动态测量listview item的高度
*
* @param listView
*/
public static void setListViewHeightBasedOnChildren(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
// pre-condition
return;
}
int totalHeight = listView.getPaddingTop()
+ listView.getPaddingBottom();
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
if (listItem instanceof ViewGroup) {
listItem.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight
+ (listView.getDividerHeight() * (listAdapter.getCount() - 1));
listView.setLayoutParams(params);
}
/**
* 动态测量listview item的高度
*
* @param listView
*/
public static void setListViewHeightBasedOnChildren1(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
return;
}
int desiredWidth = View.MeasureSpec.makeMeasureSpec(listView.getWidth(),
View.MeasureSpec.AT_MOST);
int totalHeight = 0;
View view = null;
for (int i = 0; i < listAdapter.getCount(); i++) {
view = listAdapter.getView(i, view, listView);
if (i == 0) {
view.setLayoutParams(new ViewGroup.LayoutParams(desiredWidth,
ViewGroup.LayoutParams.WRAP_CONTENT));
}
view.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED);
totalHeight += view.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight
+ (listView.getDividerHeight() * (listAdapter.getCount() - 1));
listView.setLayoutParams(params);
listView.requestLayout();
}
public static int[] getImageRealSize(Context context, int id) {
BitmapFactory.Options options = new BitmapFactory.Options();
/**
* 最关键在此,把options.inJustDecodeBounds = true;
* 这里再decodeFile(),返回的bitmap为空,但此时调用options.outHeight时,已经包含了图片的高了
*/
options.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), id, options);
int size[] = new int[2];
size[0] = options.outWidth;
size[1] = options.outHeight;
return size;
}
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// 源图片的高度和宽度
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// 计算出实际宽高和目标宽高的比率
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高
// 一定都会大于等于目标的宽和高。
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// 第一次解析将inJustDecodeBounds设置为true,来获取图片大小
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// 调用上面定义的方法计算inSampleSize值
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// 使用获取到的inSampleSize值再次解析图片
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
}
2.
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.view.View;
public class ThreePointLoadingView extends View {
// 画笔
private Paint mBallPaint;
// 宽度
private int mWidth;
// 高度
private int mHeight;
// 圆之间的距离
private float mSpace;
// 圆的半径
private float mBallRadius;
// 三个圆合起来的距离(包括间距)
private float mTotalLength;
// A圆心的x坐标
private float mABallX;
// A圆心的y坐标
private float mABallY;
// B圆心的x坐标
private float mBBallX;
// B圆心的y坐标
private float mBBallY;
// C圆心的x坐标
private float mCBallX;
// C圆心的y坐标
private float mCBallY;
// 圆心移动的距离
private float mMoveLength;
// A圆心做二阶贝塞尔曲线的起点、控制点、终点
private PointF mABallP0;
private PointF mABallP1;
private PointF mABallP2;
// A圆心贝塞尔曲线运动时的坐标
private float mABallazierX;
private float mABallazierY;
// 值动画
private ValueAnimator mAnimator;
// 值动画产生的x方向的偏移量
private float mOffsetX = 0;
// 根据mOffsetX算得的y方向的偏移量
private float mOffsetY;
// A圆的起始透明度
private int mABallAlpha = 255;
// B圆的起始透明度
private int mBBallAlpha = (int) (255 * 0.8);
// C圆的起始透明度
private int mCBallAlpha = (int) (255 * 0.6);
public ThreePointLoadingView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
mBallPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
//设置颜色
mBallPaint.setColor(ContextCompat.getColor(getContext(), R.color.material_deep_orange_a200));
mBallPaint.setStyle(Paint.Style.FILL);
mABallP0 = new PointF();
mABallP1 = new PointF();
mABallP2 = new PointF();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 考虑padding值
mWidth = measureSize(widthMeasureSpec, MeasureUtil.dip2px(getContext(), 180)) + getPaddingLeft() + getPaddingRight();
mHeight = measureSize(heightMeasureSpec, MeasureUtil.dip2px(getContext(), 180)) + getPaddingTop() + getPaddingBottom();
setMeasuredDimension(mWidth, mHeight);
// 间距为宽度10分之一
mSpace = mWidth * 1.0f / 20;
// 半径为宽度50分之一
mBallRadius = mWidth * 1.0f / 50;
// 总的长度为三个圆直径加上之间的间距
mTotalLength = mBallRadius * 6 + mSpace * 2;
// 两个圆圆心的距离
mMoveLength = mSpace + mBallRadius * 2;
// A圆心起始坐标,同时贝塞尔曲线的起始坐标也是这个
mABallazierX = mABallX = (mWidth - mTotalLength) / 2 + mBallRadius;
mABallazierY = mABallY = mHeight / 2;
// A圆心起始点,控制点,终点
mABallP0.set(mABallX, mABallY);
mABallP1.set(mABallX + mMoveLength / 2, mABallY - mMoveLength / 2);
mABallP2.set(mBBallX, mBBallY);
// B圆心的起始坐标
mBBallX = (mWidth - mTotalLength) / 2 + mBallRadius * 3 + mSpace;
mBBallY = mHeight / 2;
// C圆心的起始坐标
mCBallX = (mWidth - mTotalLength) / 2 + mBallRadius * 5 + mSpace * 2;
mCBallY = mHeight / 2;
}
@Override
protected void onDraw(Canvas canvas) {
// 根据x方向偏移量求出y方向偏移量
mOffsetY = (float) Math.sqrt(mMoveLength / 2 * mMoveLength / 2 - (mMoveLength / 2 - mOffsetX) * (mMoveLength / 2 - mOffsetX));
// 绘制B圆
mBallPaint.setAlpha(mBBallAlpha);
canvas.drawCircle(mBBallX - mOffsetX,
(float) (mBBallY + mOffsetY),
mBallRadius,
mBallPaint);
// 绘制C圆
mBallPaint.setAlpha(mCBallAlpha);
canvas.drawCircle(mCBallX - mOffsetX,
(float) (mCBallY - mOffsetY),
mBallRadius,
mBallPaint);
// 绘制A圆
mBallPaint.setAlpha(mABallAlpha);
canvas.drawCircle(mABallazierX, mABallazierY, mBallRadius, mBallPaint);
if (mAnimator == null) {
// 启动值动画
startLoading();
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
// 销毁view时取消动画,避免内存泄露
mAnimator.cancel();
}
// 开启值动画
private void startLoading() {
// 范围在0到圆心移动的距离,这个是以B圆到A圆位置为基准的
mAnimator = ValueAnimator.ofFloat(0, mMoveLength);
// 设置监听
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// B圆和C圆对应的X的偏移量
mOffsetX = (float) animation.getAnimatedValue();
float fraction = animation.getAnimatedFraction();
// B移动到A,透明度变化255*0.8->255
mBBallAlpha = (int) (255 * 0.8 + 255 * fraction * 0.2);
// C移动到B,透明度变化255*0.6->255*0.8
mCBallAlpha = (int) (255 * 0.6 + 255 * fraction * 0.2);
// A移动到C,透明度变化255->255*0.6
mABallAlpha = (int) (255 - 255 * fraction * 0.4);
// A圆的分段二阶贝塞尔曲线的处理
if (fraction < 0.5) {
// fraction小于0.5时,为A到B过程的情况
// 乘以2是因为贝塞尔公式的t范围在0到1
fraction *= 2;
// 设置当前情况的起始点、控制点、终点
mABallP0.set(mABallX, mABallY);
mABallP1.set(mABallX + mMoveLength / 2, mABallY - mMoveLength / 2);
mABallP2.set(mBBallX, mBBallY);
// 代入贝塞尔公式得到贝塞尔曲线过程的x,y坐标
mABallazierX = getBazierValue(fraction, mABallP0.x, mABallP1.x, mABallP2.x);
mABallazierY = getBazierValue(fraction, mABallP0.y, mABallP1.y, mABallP2.y);
} else {
// fraction大于等于0.5时,为A到B过程之后,再从B到C过程的情况
// 减0.5是因为t要从0开始变化
fraction -= 0.5;
// 乘以2是因为贝塞尔公式的t范围在0到1
fraction *= 2;
// 设置当前情况的起始点、控制点、终点
mABallP0.set(mBBallX, mBBallY);
mABallP1.set(mBBallX + mMoveLength / 2, mBBallY + mMoveLength / 2);
mABallP2.set(mCBallX, mCBallY);
// 代入贝塞尔公式得到贝塞尔曲线过程的x,y坐标
mABallazierX = getBazierValue(fraction, mABallP0.x, mABallP1.x, mABallP2.x);
mABallazierY = getBazierValue(fraction, mABallP0.y, mABallP1.y, mABallP2.y);
}
// 强制刷新
postInvalidate();
}
});
// 动画无限模式
mAnimator.setRepeatCount(ValueAnimator.INFINITE);
// 时长1秒
mAnimator.setDuration(1000);
// 延迟0.5秒执行
mAnimator.setStartDelay(500);
// 开启动画
mAnimator.start();
}
/**
* 二阶贝塞尔公式:B(t)=(1-t)^2*P0+2*t*(1-t)*P1+t^2*P2,(t∈[0,1])
*/
private float getBazierValue(float fraction, float p0, float p1, float p2) {
return (1 - fraction) * (1 - fraction) * p0 + 2 * fraction * (1 - fraction) * p1 + fraction * fraction * p2;
}
// 测量尺寸
private int measureSize(int measureSpec, int defaultSize) {
final int mode = MeasureSpec.getMode(measureSpec);
final int size = MeasureSpec.getSize(measureSpec);
if (mode == MeasureSpec.EXACTLY) {
return size;
} else if (mode == MeasureSpec.AT_MOST) {
return Math.min(size, defaultSize);
}
return size;
}
}
3.main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<example.threepointloadingview.ThreePointLoadingView
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
4.Maintivity
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
5效果图:
猛戳这里 :studio点击下载
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/12867.html