Android 仿UC浏览器三点加载效果

导读:本篇文章讲解 Android 仿UC浏览器三点加载效果,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

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

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!