Android动态壁纸详解

动态壁纸

Livewallpaper(动态壁纸): 首先动态壁纸并不是GIF图片,而是一个独立的应用程序,本质是一个Service,甚至可以没有桌面图标。


直接看AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.qwq.clocklivewallpaper">

    <application
        android:allowBackup="true"
        android:icon="@drawable/clock"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <!-- 主界面Activity.可以去掉。动态壁纸应用是可以允许没有Activity的 -->
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- 动态壁纸设置界面,可有可无,一般壁纸应用会有,作用是让用户自定义动态壁纸 -->
        <activity
            android:name=".SettingsActivity"
            android:exported="true"
            android:label="@string/app_name">
        </activity>

        <!-- 动态壁纸本质就是Service,所以此类为动态壁纸核心-->
        <service
            android:name=".ClockWallpaperService"
            android:enabled="true"
            android:label="@string/wallpaper_name" //下面第一张图片中label
            android:permission="android.permission.BIND_WALLPAPER">//动态壁纸必须加此权限

            <intent-filter>
                //系统就是通过APK的这个action把其当做一个动态墙纸。
              <action android:name="android.service.wallpaper.WallpaperService"></action>
            </intent-filter>

            <!-- android:resource 指定的xml很重要!!!后面会单独介绍 -->
            <meta-data
                android:name="android.service.wallpaper"
                android:resource="@xml/clock_wallpaper">
            </meta-data>

        </service>
    </application>

</manifest>

clock_wallpaper.xml

<?xml version="1.0" encoding="UTF-8"?>
<wallpaper xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/wallpaper_description"
    android:settingsActivity="com.qwq.clocklivewallpaper.SettingsActivity"
    android:thumbnail="@drawable/ic_preview" />

android:thumbnail 动态壁纸列表中的图标,
android:description 动态壁纸的简单介绍文字,有的手机可能不显示
动态壁纸列表图片

android:settingsActivity 指定一个Activity。系统会检测动态壁纸应用有没有此属性。如果有则和下方的图片显示效果一样(两个Button),点击下图中设定Button可跳转至指定的Activity(设置界面)。如果没有此属性,就只有一个设置壁纸Button。可以去掉此属性看一下效果。
动态壁纸设置界面图片


WallpaperService(核心)

实现动态壁纸必须继承WallpaperService,且重载onCreateEngine方法。onCreateEngine()方法只需返回一个Engine的子类对象就可以了,所以动态壁纸应用的主要工作就是实现Engine的子类。其原理是使用surfaceview不断更新,实现动态壁纸效果。不多说了,直接贴代码,重要的方法都会有所说明。

package com.qwq.clocklivewallpaper;

import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;

import java.util.Date;

public class ClockWallpaperService extends WallpaperService {

    static final String TAG = ClockWallpaperService.class.getSimpleName();
    static final int POINTER_WIDTH = 9;

  //  onCreateEngine()方法需返回一个Engine的子类对象就可以了,
  //  所以动态壁纸应用的主要工作就是实现Engine的子类
  @Override
  public Engine onCreateEngine() {
        Log.d(TAG, "onCreateEngine");
        return new ClockWallpaperEngine();
    }


    //实现Engine的子类,本文的重中之重,动态壁纸就是在此具体实现的
  private class ClockWallpaperEngine extends Engine implements 
  OnSharedPreferenceChangeListener {
        private final Handler handler = new Handler();
        // surfacewive使用线程更新UI,所以我们可使用Runnable接口创建一个线程,把具体绘制方法放进去,
        // 下文会使用handler调用
        private final Runnable drawRunner = new Runnable() {
            @Override
            public void run() {
                draw();
            }
        };

        private Paint paint;
        private int width;
        private int height;
        private boolean isVisible = true;
        private boolean isShowSecond;
        private ClockView clockView;
        private SharedPreferences sp;

 //构造函数,初始化动态壁纸
 public ClockWallpaperEngine() {
            Log.d(TAG, "ClockWallpaperEngine");
            initSp();
            initPaint();
            startDrawClock();
        }

  public void initSp() {
         sp = PreferenceManager.getDefaultSharedPreferences(ClockWallpaperService.this);
         sp.registerOnSharedPreferenceChangeListener(this);
         isShowSecond = sp.getBoolean(SettingsActivity.IS_SHOW_SECOND, true);
        }

  public void initPaint() {
            paint = new Paint();
            paint.setAntiAlias(true);
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeWidth(POINTER_WIDTH);
        }

   public void startDrawClock() {
            clockView = new ClockView(getApplicationContext());
            handler.post(drawRunner);
        }

   @Override
   public void onCreate(SurfaceHolder surfaceHolder) {
            super.onCreate(surfaceHolder);
            Log.d(TAG, "onCreate");
        }


   //监听是否可见变化,可见时开始更新UI,不可见时停止刷新
   @Override
   public void onVisibilityChanged(boolean visible) {
            this.isVisible = visible;
            if (visible) {
                handler.post(drawRunner);
            } else {
                handler.removeCallbacks(drawRunner);
            }
        }

   @Override
   public void onSurfaceDestroyed(SurfaceHolder holder) {
            super.onSurfaceDestroyed(holder);
            isVisible = false;
            handler.removeCallbacks(drawRunner);
            sp.unregisterOnSharedPreferenceChangeListener(this);
        }


   @Override
   public void onSurfaceChanged(SurfaceHolder holder,int format,int width,int height){    
            this.width = width;
            this.height = height;
            super.onSurfaceChanged(holder, format, width, height);
        }

   //绘制,每个200毫秒刷新界面
   private void draw() {
            SurfaceHolder holder = getSurfaceHolder();
            Canvas canvas = null;
            try {
                canvas = holder.lockCanvas();
                if (canvas != null) {
                    drawClock(canvas);
                }
            } finally {
                if (canvas != null) {
                    holder.unlockCanvasAndPost(canvas);
                }
            }

            handler.removeCallbacks(drawRunner);
            if (isVisible) {
                handler.postDelayed(drawRunner, 200);
            }
        }

  //具体绘制钟表
  private void drawClock(Canvas canvas) {
       canvas.drawColor(Color.WHITE);   //绘制整个动态壁纸的背景颜色
       clockView.config(width / 2, height / 2, (int) (width * 0.8f), new Date(), 
       paint, isShowSecond);
       clockView.draw(canvas);
        }

  @Override
  public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String 
  key){ 
     //监听SharedPreference变化,改变动态壁纸样式
     if (SettingsActivity.IS_SHOW_SECOND.equals(key)) {
     isShowSecond = sharedPreferences.getBoolean(SettingsActivity.IS_SHOW_SECOND,  
     true);
            }
        }

   @Override
   public void onTouchEvent(MotionEvent event) {
            super.onTouchEvent(event);
            // 可以在这里做一些与用户交互的操作,例如动态星星壁纸,用户触摸屏幕时增加星星数量。
            Log.d(TAG, "onTouchEvent");
        }
    }

}


ClockView(钟表的具体绘制)

package com.qwq.clocklivewallpaper;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.Log;
import android.view.View;

import java.util.Calendar;
import java.util.Date;

public class ClockView extends View {

    static final String TAG = ClockView.class.getSimpleName();
    private float x;
    private float y;
    private int radius;
    private Calendar cal;
    private Paint paint;
    private Bitmap clockDial = BitmapFactory.decodeResource(getResources(),R.drawable.clock_bg);
    private int sizeScaled = -1;
    private Bitmap clockDialScaled;
    private boolean isShowSecond;

    public ClockView(Context context) {
        super(context);
        cal = Calendar.getInstance();
    }

//设置钟表宽高等属性
public void config(float x, float y, int size, Date date, Paint paint, boolean 
    isShowSecond) {
        this.x = x;
        this.y = y;
        this.paint = paint;
        this.isShowSecond = isShowSecond;

        cal.setTime(date);

        if (size != sizeScaled) {
            clockDialScaled = Bitmap.createScaledBitmap(clockDial, size, size, false);
            radius = size / 2;
        }
    }

//具体绘制钟表的指针
protected void onDraw(Canvas canvas) {
   Log.d(TAG,"onDraw:"+canvas);
   super.onDraw(canvas);
   if (paint != null) {
            canvas.drawBitmap(clockDialScaled, x - radius, y - radius, null);

            float sec = cal.get(Calendar.SECOND);
            float min = cal.get(Calendar.MINUTE);
            float hour = cal.get(Calendar.HOUR_OF_DAY);


   paint.setColor(Color.RED);
   canvas.drawLine(x, y,
   (float)(x + (radius * 0.5f)*Math.cos(Math.toRadians((hour / 12.0f * 360.0f) - 90f))),
   (float)(y + (radius * 0.5f)*Math.sin(Math.toRadians((hour / 12.0f * 360.0f) -90f))),   
   paint);
   canvas.save();

    paint.setColor(Color.BLUE);
    canvas.drawLine(x, y, 
    (float)(x + (radius * 0.6f)*Math.cos(Math.toRadians((min / 60.0f * 360.0f) - 90f))),
    (float)(y + (radius * 0.6f)*Math.sin(Math.toRadians((min / 60.0f * 360.0f) - 90f))),  
    paint);
    canvas.save();

    if (isShowSecond) {
       paint.setColor(Color.GREEN);
       canvas.drawLine(x, y,
       (float)(x + (radius * 0.7f)*Math.cos(Math.toRadians((sec/60.0f * 360.0f) - 90f))),
       (float)(y + (radius * 0.7f)*Math.sin(Math.toRadians((sec/60.0f * 360.0f) - 90f))),   
       paint);
          }
        }
    }

}

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android动态分区是一种在存储设备上为应用程序和数据进行分区管理的技术。它允许用户将存储设备的空间灵活地划分为多个分区,并将不同的应用程序和数据存储在不同的分区中。 首先,Android动态分区可以提供更灵活的存储管理。通常情况下,存储设备被划分为内部存储和外部存储。内部存储用于安装应用程序和存储数据,而外部存储则用于存储媒体文件等。通过动态分区,用户可以根据自己的需求,自由地划分存储设备的空间,使得内部存储和外部存储可以互相调整大小,从而更好地满足用户的存储需求。 其次,动态分区可以提供更好的隔离保护。每个应用程序和数据都被存储在独立的分区中,这样可以避免不同应用程序之间的干扰和冲突。当一个应用程序出现问题时,只需要清除或重置相关的分区,而不会影响其他应用程序的正常运行。这种隔离性可以增加系统的稳定性和安全性。 另外,Android动态分区还可以优化存储空间的使用。系统会将一些常用的应用程序和数据放置在内部存储的快速区域,这样可以提高应用程序和数据的访问速度。同时,一些很少使用的应用程序和数据可以被移动到外部存储的缓慢区域,从而释放内部存储空间,让更常用的应用程序和数据能够更好地运行。 总的来说,Android动态分区是一种可以提供灵活存储管理、更好的隔离保护和优化存储空间使用的技术。它可以为用户提供更好的使用体验,并提高系统的稳定性和安全性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值