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


下一篇将会讲述切换城市


相关文章
|
1月前
|
XML Java 数据库
安卓项目:app注册/登录界面设计
本文介绍了如何设计一个Android应用的注册/登录界面,包括布局文件的创建、登录和注册逻辑的实现,以及运行效果的展示。
152 0
安卓项目:app注册/登录界面设计
|
2月前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android多线程编程的重要性及其实现方法,涵盖了基本概念、常见线程类型(如主线程、工作线程)以及多种多线程实现方式(如`Thread`、`HandlerThread`、`Executors`、Kotlin协程等)。通过合理的多线程管理,可大幅提升应用性能和用户体验。
131 15
一个Android App最少有几个线程?实现多线程的方式有哪些?
|
2月前
|
存储 开发工具 Android开发
使用.NET MAUI开发第一个安卓APP
【9月更文挑战第24天】使用.NET MAUI开发首个安卓APP需完成以下步骤:首先,安装Visual Studio 2022并勾选“.NET Multi-platform App UI development”工作负载;接着,安装Android SDK。然后,创建新项目时选择“.NET Multi-platform App (MAUI)”模板,并仅针对Android平台进行配置。了解项目结构,包括`.csproj`配置文件、`Properties`配置文件夹、平台特定代码及共享代码等。
188 2
|
2月前
|
XML Android开发 数据格式
🌐Android国际化与本地化全攻略!让你的App走遍全球无障碍!🌍
在全球化背景下,实现Android应用的国际化与本地化至关重要。本文以一款旅游指南App为例,详细介绍如何通过资源文件拆分与命名、适配布局与方向、处理日期时间及货币格式、考虑文化习俗等步骤,完成多语言支持和本地化调整。通过邀请用户测试并收集反馈,确保应用能无缝融入不同市场,提升用户体验与满意度。
111 3
|
2月前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android应用开发中的多线程编程,涵盖基本概念、常见实现方式及最佳实践。主要内容包括主线程与工作线程的作用、多线程的多种实现方法(如 `Thread`、`HandlerThread`、`Executors` 和 Kotlin 协程),以及如何避免内存泄漏和合理使用线程池。通过有效的多线程管理,可以显著提升应用性能和用户体验。
74 10
|
1月前
|
安全 网络安全 Android开发
深度解析:利用Universal Links与Android App Links实现无缝网页至应用跳转的安全考量
【10月更文挑战第2天】在移动互联网时代,用户经常需要从网页无缝跳转到移动应用中。这种跳转不仅需要提供流畅的用户体验,还要确保安全性。本文将深入探讨如何利用Universal Links(仅限于iOS)和Android App Links技术实现这一目标,并分析其安全性。
246 0
|
2月前
|
XML 数据库 Android开发
10分钟手把手教你用Android手撸一个简易的个人记账App
该文章提供了使用Android Studio从零开始创建一个简单的个人记账应用的详细步骤,包括项目搭建、界面设计、数据库处理及各功能模块的实现方法。
|
1月前
|
JSON 小程序 JavaScript
uni-app开发微信小程序的报错[渲染层错误]排查及解决
uni-app开发微信小程序的报错[渲染层错误]排查及解决
549 7
|
1月前
|
小程序 JavaScript 前端开发
uni-app开发微信小程序:四大解决方案,轻松应对主包与vendor.js过大打包难题
uni-app开发微信小程序:四大解决方案,轻松应对主包与vendor.js过大打包难题
662 1
|
25天前
|
小程序 数据挖掘 UED
开发1个上门家政小程序APP系统,都有哪些功能?
在快节奏的现代生活中,家政服务已成为许多家庭的必需品。针对传统家政服务存在的问题,如服务质量不稳定、价格不透明等,我们历时两年开发了一套全新的上门家政系统。该系统通过完善信用体系、提供奖励机制、优化复购体验、多渠道推广和多样化盈利模式,解决了私单、复购、推广和盈利四大痛点,全面提升了服务质量和用户体验,旨在成为家政行业的领导者。