Android 天气APP(六)旋转风车显示风力、风向

简介: Android 天气APP(六)旋转风车显示风力、风向

8. 旋转风车


这个时候就要用到自定义View了,这个工具类的代码也并不是我自己写的,而是网络上找的,


① 样式


在模块的res文件夹下的values文件下新建一个styles.xml


20200404202618936.png


里面的代码如下:


<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--白色风车-->
    <declare-styleable name="WhiteWindmills">
        <attr name="windColor" format="reference|color" />
    </declare-styleable>
</resources>


② 自定义View


然后就是自定义VIew了,


20200404202814459.png


在模块的com.llw.mvplibrary下面创建一个view的包,包下创建一个名为WhiteWindmills的类。


代码如下:


package com.llw.mvplibrary.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
import com.llw.mvplibrary.R;
import java.lang.ref.WeakReference;
/**
 * 白色风车
 */
public class WhiteWindmills extends View {
    /**
     * 叶片的长度
     */
    private float mBladeRadius;
    /**
     * 风车叶片旋转中心x
     */
    private int mCenterY;
    /**
     * 风车叶片旋转中心y
     */
    private int mCenterX;
    /**
     * 风车旋转中心点圆的半径
     */
    private float mPivotRadius;
    private Paint mPaint = new Paint();
    /**
     * 风车旋转时叶片偏移的角度
     */
    private int mOffsetAngle;
    private Path mPath = new Path();
    /**
     * 风车支柱顶部和底部为了画椭圆的矩形
     */
    private RectF mRect = new RectF();
    /**
     * 控件的宽
     */
    private int mWid;
    /**
     * 控件高
     */
    private int mHei;
    /**
     * 控件颜色
     */
    private int mColor;
    private MsgHandler mHandler = new MsgHandler(this);
    public WhiteWindmills(Context context) {
        this(context, null);
    }
    public WhiteWindmills(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public WhiteWindmills(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context, attrs);
    }
    private void initView(Context context, AttributeSet attrs) {
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.WhiteWindmills);
        if (array != null) {
            mColor = array.getColor(R.styleable.WhiteWindmills_windColor, Color.WHITE);
            array.recycle();
        }
        //抗锯齿
        mPaint.setAntiAlias(true);
        mPaint.setColor(mColor);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int heiMeasure = MeasureSpec.getSize(heightMeasureSpec);
        int heiMode = MeasureSpec.getMode(heightMeasureSpec);
        int widMode = MeasureSpec.getMode(widthMeasureSpec);
        int widMeasure = MeasureSpec.getSize(widthMeasureSpec);
        mWid = widMeasure;
        mHei = heiMeasure;
        mCenterY = mWid / 2;
        mCenterX = mWid / 2;
        mPivotRadius = (float) mWid / (float) 40;
        mBladeRadius = mCenterY - 2 * mPivotRadius;
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //画扇叶旋转的中心
        drawPivot(canvas);
        //画扇叶
        drawWindBlade(canvas);
        //画底部支柱
        drawPillar(canvas);
    }
    /**
     * 画风车支点
     *
     * @param canvas
     */
    private void drawPivot(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL);
        canvas.drawCircle(mCenterX, mCenterY, mPivotRadius, mPaint);
    }
    /**
     * 画叶片
     *
     * @param canvas
     */
    private void drawWindBlade(Canvas canvas) {
        canvas.save();
        mPath.reset();
        //根据偏移量画初始时画布的位置
        canvas.rotate(mOffsetAngle, mCenterX, mCenterY);
        //画三角形扇叶
        mPath.moveTo(mCenterX, mCenterY - mPivotRadius);// 此点为多边形的起点
        mPath.lineTo(mCenterX, mCenterY - mPivotRadius - mBladeRadius);
        mPath.lineTo(mCenterX + mPivotRadius, mPivotRadius + mBladeRadius * (float) 2 / (float) 3);
        mPath.close(); // 使这些点构成封闭的多边形
        canvas.drawPath(mPath, mPaint);
        //旋转画布120度,画第二个扇叶
        canvas.rotate(120, mCenterX, mCenterY);
        canvas.drawPath(mPath, mPaint);
        //旋转画布120度,画第三个扇叶
        canvas.rotate(120, mCenterX, mCenterY);
        canvas.drawPath(mPath, mPaint);
        canvas.restore();
    }
    /**
     * 画支柱
     *
     * @param canvas
     */
    private void drawPillar(Canvas canvas) {
        mPath.reset();
        //画上下半圆之间的柱形
        mPath.moveTo(mCenterX - mPivotRadius / 2, mCenterY + mPivotRadius + mPivotRadius / 2);
        mPath.lineTo(mCenterX + mPivotRadius / 2, mCenterY + mPivotRadius + mPivotRadius / 2);
        mPath.lineTo(mCenterX + mPivotRadius, mHei - 2 * mPivotRadius);
        mPath.lineTo(mCenterX - mPivotRadius, mHei - 2 * mPivotRadius);
        mPath.close();
        //画顶部半圆
        mRect.set(mCenterX - mPivotRadius / 2, mCenterY + mPivotRadius, mCenterX + mPivotRadius / 2, mCenterY + 2 * mPivotRadius);
        mPath.addArc(mRect, 180, 180);
        //画底部半圆
        mRect.set(mCenterX - mPivotRadius, mHei - 3 * mPivotRadius, mCenterX + mPivotRadius, mHei - mPivotRadius);
        mPath.addArc(mRect, 0, 180);
        canvas.drawPath(mPath, mPaint);
    }
    /**
     * 开始旋转
     */
    public void startRotate() {
        stop();
        mHandler.sendEmptyMessageDelayed(0, 10);
    }
    /**
     * 停止旋转
     */
    public void stop() {
        mHandler.removeMessages(0);
    }
    static class MsgHandler extends Handler {
        private WeakReference<WhiteWindmills> mView;
        MsgHandler(WhiteWindmills view) {
            mView = new WeakReference<WhiteWindmills>(view);
        }
        @Override
        public void handleMessage(Message msg) {
            WhiteWindmills view = mView.get();
            if (view != null) {
                view.handleMessage(msg);
            }
        }
    }
    private void handleMessage(Message msg) {
        if (mOffsetAngle >= 0 && mOffsetAngle < 360) {
            mOffsetAngle = mOffsetAngle + 1;
        } else {
            mOffsetAngle = 1;
        }
        invalidate();
        startRotate();
    }
}


这个部分完成之后,修改布局,将这一块加进去。


③ 使用与运行显示


20200404203737431.png


这部分代码如下:


          <!--风力展示-->
                    <LinearLayout
                        android:orientation="horizontal"
                        android:padding="20dp"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content">
                        <RelativeLayout
                            android:id="@+id/rl_wind"
                            android:layout_width="130dp"
                            android:layout_height="120dp">
                            <!--大风车-->
                            <com.llw.mvplibrary.view.WhiteWindmills
                                android:id="@+id/ww_big"
                                android:layout_width="100dp"
                                android:layout_height="120dp" />
                            <!--小风车-->
                            <com.llw.mvplibrary.view.WhiteWindmills
                                android:id="@+id/ww_small"
                                android:layout_width="50dp"
                                android:layout_height="60dp"
                                android:layout_alignParentBottom="true"
                                android:layout_alignParentRight="true"
                                />
                        </RelativeLayout>
                        <LinearLayout
                            android:gravity="center"
                            android:orientation="vertical"
                            android:layout_weight="1"
                            android:layout_width="0dp"
                            android:layout_height="match_parent">
                            <!--风向-->
                            <TextView
                                android:id="@+id/tv_wind_direction"
                                android:textColor="#FFF"
                                android:textSize="@dimen/sp_14"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"/>
                            <!--风力-->
                            <TextView
                                android:layout_marginTop="20dp"
                                android:id="@+id/tv_wind_power"
                                android:textColor="#FFF"
                                android:textSize="@dimen/sp_14"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"/>
                        </LinearLayout>
                    </LinearLayout>


代码中:


20200404204805699.png


风力的数据其实在天气数据的返回值就有了,就是第一个接口,接下来修改代码

20200404205353825.png


代码如下:


      tvWindDirection.setText("风向     " + response.body().getHeWeather6().get(0).getNow().getWind_dir());//风向
            tvWindPower.setText("风力     " + response.body().getHeWeather6().get(0).getNow().getWind_sc() + "级");//风力
            wwBig.startRotate();//大风车开始转动
            wwSmall.startRotate();//小风车开始转动


记得在页面销毁的时候停止这个风车:


  /**
     * 页面销毁时
     */
    @Override
    public void onDestroy() {
        wwBig.stop();//停止大风车
        wwSmall.stop();//停止小风车
        super.onDestroy();
    }


运行一下:


20200404210556971.png


下一篇将会讲述切换城市


相关文章
|
13天前
|
ARouter IDE 开发工具
Android面试题之App的启动流程和启动速度优化
App启动流程概括: 当用户点击App图标,Launcher通过Binder IPC请求system_server启动Activity。system_server指示Zygote fork新进程,接着App进程向system_server申请启动Activity。经过Binder通信,Activity创建并回调生命周期方法。启动状态分为冷启动、温启动和热启动,其中冷启动耗时最长。优化技巧包括异步初始化、避免主线程I/O、类加载优化和简化布局。
28 3
Android面试题之App的启动流程和启动速度优化
|
11天前
|
缓存 JSON 网络协议
Android面试题:App性能优化之电量优化和网络优化
这篇文章讨论了Android应用的电量和网络优化。电量优化涉及Doze和Standby模式,其中应用可能需要通过用户白名单或电池广播来适应限制。Battery Historian和Android Studio的Energy Profile是电量分析工具。建议减少不必要的操作,延迟非关键任务,合并网络请求。网络优化包括HTTPDNS减少DNS解析延迟,Keep-Alive复用连接,HTTP/2实现多路复用,以及使用protobuf和gzip压缩数据。其他策略如使用WebP图像格式,按网络质量提供不同分辨率的图片,以及启用HTTP缓存也是有效手段。
33 9
|
12天前
|
XML 监控 安全
Android App性能优化之卡顿监控和卡顿优化
本文探讨了Android应用的卡顿优化,重点在于布局优化。建议包括将耗时操作移到后台、使用ViewPager2实现懒加载、减少布局嵌套并利用merge标签、使用ViewStub减少资源消耗,以及通过Layout Inspector和GPU过度绘制检测来优化。推荐使用AsyncLayoutInflater异步加载布局,但需注意线程安全和不支持特性。卡顿监控方面,提到了通过Looper、ChoreographerHelper、adb命令及第三方工具如systrace和BlockCanary。总结了Choreographer基于掉帧计算和BlockCanary基于Looper监控的原理。
21 3
|
15天前
|
安全 JavaScript 前端开发
kotlin开发安卓app,JetPack Compose框架,给webview新增一个按钮,点击刷新网页
在Kotlin中开发Android应用,使用Jetpack Compose框架时,可以通过添加一个按钮到TopAppBar来实现WebView页面的刷新功能。按钮位于右上角,点击后调用`webViewState?.reload()`来刷新网页内容。以下是代码摘要:
|
10天前
|
Java Android开发 Kotlin
Android面试题:App性能优化之Java和Kotlin常见的数据结构
Java数据结构摘要:ArrayList基于数组,适合查找和修改;LinkedList适合插入删除;HashMap1.8后用数组+链表/红黑树,初始化时预估容量可避免扩容。SparseArray优化查找,ArrayMap减少冲突。 Kotlin优化摘要:Kotlin的List用`listOf/mutableListOf`,Map用`mapOf/mutableMapOf`,支持操作符重载和扩展函数。序列提供懒加载,解构用于遍历Map,扩展函数默认参数增强灵活性。
14 0
|
18天前
|
前端开发 JavaScript Android开发
手机APP开发|基于安卓APP实现掌上党支部——党员app
手机APP开发|基于安卓APP实现掌上党支部——党员app
|
4天前
|
安全 Android开发 iOS开发
探索安卓与iOS开发的差异:平台特性与用户体验的深度对比
在移动应用开发的广阔天地中,安卓和iOS两大平台各占半壁江山。本文旨在通过数据驱动的分析方法,深入探讨这两大操作系统在开发环境、用户界面设计及市场表现等方面的差异。引用最新的行业报告和科研数据,结合技术专家的观点,本文将提供对开发者和市场分析师均有价值的洞见。
|
11天前
|
安全 Java Android开发
安卓开发中的新趋势:Kotlin与Jetpack的完美结合
【6月更文挑战第20天】在不断进化的移动应用开发领域,Android平台以其开放性和灵活性赢得了全球开发者的青睐。然而,随着技术的迭代,传统Java语言在Android开发中逐渐显露出局限性。Kotlin,一种现代的静态类型编程语言,以其简洁、安全和高效的特性成为了Android开发中的新宠。同时,Jetpack作为一套支持库、工具和指南,旨在帮助开发者更快地打造优秀的Android应用。本文将探讨Kotlin与Jetpack如何共同推动Android开发进入一个新的时代,以及这对开发者意味着什么。
|
7天前
|
Java 开发工具 Android开发
探索Android与iOS开发的差异:平台选择对项目成功的影响
在移动应用开发的广阔天地中,Android和iOS两大平台各自占据着半壁江山。本文将深入探讨这两个平台在开发过程中的关键差异点,包括编程语言、开发工具、用户界面设计、性能优化以及市场覆盖等方面。通过对这些关键因素的比较分析,旨在为开发者提供一个清晰的指南,帮助他们根据项目需求和目标受众做出明智的平台选择。
|
7天前
|
编解码 Android开发 iOS开发
深入探索Android与iOS开发的差异与挑战
【6月更文挑战第24天】在移动应用开发的广阔舞台上,Android和iOS两大操作系统扮演着主角。它们各自拥有独特的开发环境、工具集、用户基础及市场策略。本文将深度剖析这两个平台的开发差异,并探讨开发者面临的挑战,旨在为即将踏入或已在移动开发领域奋斗的开发者提供一份实用指南。
30 13