Android中利用动态加载实现手机淘宝的节日特效

相信去年圣诞节打开过手机淘宝的童鞋都会对当时的特效记忆犹新吧:全屏飘雪,旁边还有个小雪人来控制八音盒背景音乐的播放,让人有种身临其境的感觉,甚至忍不住想狠狠购物了呢(误),大概就是下面这个样子滴:

嗯,确实很炫,那么我们一步步去分析是如何实现的:

一、实现下雪的 View

首先,最上面一层的全屏雪花极有可能是一个顶层的View,而这个View是通过动态加载去控制显示的(不更新淘宝也能看到这个效果)。那么我们先得实现雪花效果的 View,人生苦短,拿来就用。打开 gank.io,搜索"雪花":

看样子第7个库就是我们想要的了,点进源码,直接 download 不解释,记得 star 一个支持作者。那么现在我们的项目中就有一个完整的下雪效果 View 了。

二、实现雪人播放器 View

这个一张雪人图片+一个按钮即可实现,就不多解释了。接下来需要一段圣诞节音频,直接进行在线音频播放无疑是节省空间的好方案。『我的滑板鞋』烘托出的寂寞而甜蜜的氛围无疑是最适合圣诞节的,因此我们得到了『神曲』URL 一枚:
http://cdn.ifancc.com/TomaToDo/bgms/my_hbx.mp3

接下来要找一个小雪人的图片当作播放器的背景,那么阿姆斯特朗...不对,是这个:

嗯,相当可爱喜庆。那么播放器核心代码如下:

package com.kot32.christmasview.player;
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Toast;
import com.kot32.christmasview.R;
import java.io.IOException;
/**
 * Created by kot32 on 16/12/8.
 */
public class MyPlayer extends View {
 public MediaPlayer mediaPlayer;
 public MyPlayer(Context context) {
  super(context);
  init();
 }
 public MyPlayer(Context context, AttributeSet attrs) {
  super(context, attrs);
  init();
 }
 private void init() {
  setBackgroundResource(R.drawable.pig);
  mediaPlayer = new MediaPlayer();
  mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
  playUrl("http://172.20.248.106/IXC5b415fcacfc3c439e25a3e74533d2239/TomaToDo/bgms/my_hbx.mp3");
  Toast.makeText(getContext(), "开始播放", Toast.LENGTH_SHORT).show();
  setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View v) {
    if (!mediaPlayer.isPlaying()) {
     mediaPlayer.start();
     Toast.makeText(getContext(), "继续播放", Toast.LENGTH_SHORT).show();
    } else {
     mediaPlayer.pause();
     Toast.makeText(getContext(), "暂停播放", Toast.LENGTH_SHORT).show();
    }
   }
  });
 }
 public void playUrl(String videoUrl) {
  try {
   mediaPlayer.reset();
   mediaPlayer.setDataSource(videoUrl);
   mediaPlayer.prepare();//prepare之后自动播放
   mediaPlayer.start();
  } catch (IllegalArgumentException e) {
   e.printStackTrace();
  } catch (IllegalStateException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
 @Override
 protected void onDetachedFromWindow() {
  super.onDetachedFromWindow();
  try {
   mediaPlayer.stop();
   mediaPlayer.release();
  }catch (Exception e){
   e.printStackTrace();
  }
 }
}

三、动态加载思路

上面基本实现了在本地的雪花以及播放音乐效果,那么在不更新主程序的情况下,如何将这两个View动态加载到主程序当中去呢?

首先我们明白,Android 的DexClassloader 是拥有加载任意APK 中任意类的能力的,只是有以下限制:

加载出的Activity 由于不在宿主 Manifest 文件中声明,因此框架无法找到并初始化这个Activity。

加载出的Activity 不具备生命周期,理由同上。

加载出的类的Resource 文件id 会和主程序混淆在一起。

由于我们只是加载View,并不是加载整个Activity,所以前两个问题并不会遇到,而第三个问题可以想办法解决掉。

在主程序中我们也要做这三件事:

把能够装载View的ViewGroup 的空位留出来

去获取更新的patch包

把View 从apk包中加载出来之后,放进留好的ViewGroup 中。

这样一来,不仅是圣诞节,在之后的各种活动上都可以在线去加载活动的View。

四、开始加载

在加载View 之前,首先要意识到这个View 是引用了图片资源的(小猪图片),因此我们要解决资源问题:

private void initResource() {
  Resources resources = getContext().getResources();
  try {
   AssetManager newManager = AssetManager.class.newInstance();
   Method addAssetPath = newManager.getClass().getMethod("addAssetPath", String.class);
   addAssetPath.invoke(newManager, DynamicViewManager.getInstance().getUpdateFileFullPath());
   Resources newResources = new Resources(newManager,
     resources.getDisplayMetrics(), resources.getConfiguration());
   Reflect.onObject(getContext()).set("mResources", newResources);
  } catch (Exception e) {
   e.printStackTrace();
  }
 }

上面代码的作用是:把添加了外部更新包路径的资源管理器赋值给了App原来的资源管理器,也就是说现在可以在宿主中访问插件资源了。

核心加载代码如下:

DexClassLoader classLoader = new DexClassLoader(apkFile.getAbsolutePath()
     , "dex_out_put_dir"
     , null
     , getClass().getClassLoader());
Class newViewClazz = classLoader.loadClass("view's package name");
Constructor con = newViewClazz.getConstructor(Context.class);
//first use Activity's Resource lie to View
if (dynamicView == null) {
 dynamicView = (View) con.newInstance(getContext());
}
//Replace the View's mResources and recovery the Activity's avoid disorder of Resources
Reflect.onObject(getContext()).set("mResources", null);
getContext().getResources();
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(DisplayUtil.dip2px(getContext(), viewInfo.layoutParams.width),
   DisplayUtil.dip2px(getContext(), viewInfo.layoutParams.height));
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
addView(dynamicView, layoutParams);

中间对 mResources 的操作的作用是:将宿主的Activity 的mResources 重置,避免在Activity 中使用资源时和插件冲突。

然而机智的我已经把更新包下载、版本管理、动态加载都封装好了,所以正确的加载方式是:

引用它:https://github.com/kot32go/dynamic-load-view

然后:

1.宿主声明:

<RelativeLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="@drawable/tb_bg"
 >
 <com.kot32.dynamicloadviewlibrary.core.DynamicViewGroup
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  app:uuid="activity_frame">
  <TextView
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text="原始页面"
   />
 </com.kot32.dynamicloadviewlibrary.core.DynamicViewGroup>
 <com.kot32.dynamicloadviewlibrary.core.DynamicViewGroup
  android:layout_width="60dp"
  android:layout_height="60dp"
  android:layout_alignParentRight="true"
  android:layout_centerVertical="true"
  app:uuid="activity_player">
 </com.kot32.dynamicloadviewlibrary.core.DynamicViewGroup>
</RelativeLayout>

以上声明了主界面的布局,当然,在动态加载之前除了原有的"原始页面"TextView,是不会有任何其他东西的,也就是圣诞节来临之前的程序。注意:uuid 会和在线包相匹配。

2.打插件包

其实就是把之前包含了我们所写的两个View(雪花和雪人)的程序打包成apk。可以不签名。

3.把插件包放到服务器

在服务器返回的JSON中声明插件包地址和动态View 的一些参数,这里的演示程序请求地址为:

http://tomatodo.ifancc.com/php/dynamicView.php

返回值为:

{
 "version": 54,
 "downLoadPath": "http://obfgb7oet.bkt.clouddn.com/patch106.apk",
 "fileName": "patch106.apk",
 "viewInfo": [
 {
  "packageName": "com.kot32.testdynamicviewproject.snow.widgets.SnowingView",
  "uuid": "activity_frame",
  "layoutParams": {
  "width": -1,
  "height": -1
  }
 },
 {
  "packageName": "com.kot32.testdynamicviewproject.player.MyPlayer",
  "uuid": "activity_player",
  "layoutParams": {
  "width": -1,
  "height": -1
  }
 }
 ]
}

我们声明了这次在线包的版本,每个View 的包名和布局参数, 以及最重要的 和宿主程序中声明对齐的uuid。

以上所述是小编给大家介绍的Android中利用动态加载实现手机淘宝的节日特效,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Android 中动态加载.jar的实现步骤

    首先第一个是 jar 文件的制作,Java 里面直接把 .class 文件打包到 .jar 文件里面就可以了,但是 Android 的 Dalvik VM 是不认 Java 的 byte code 的,所以不能直接这么打包,而要用 dx 工具转成 Dalvik byte code 才可以.当然,dx 工具转了之后,jar 包里面就不 是 .class 文件了,而是 .dex 文件. 第二个是,Android 里面虽然也提供了 URLClassLoader 的实现,但是并不能用.要动态加载其它类,

  • Android应用开发中Fragment的静态加载与动态加载实例

    1.Fragment的静态使用 Fragment是作为Activity的UI的一部分,它内嵌在Activity中,多个Fragment可以把一个Activity分成多个部分,这在大屏幕手机或者平板电脑中会比较多的用到,这样就不用使用多个Activity来切换这么麻烦了.当然Fragment也可以不显示,只在后台处理一些数据,这篇文章中就暂时不谈到这个.以下来看怎么静态地在Activity的布局文件中添加Fragment. 自定义的Fragment通常要继承Fragment这个类,也有一些特殊的是

  • Android实现Listview异步加载网络图片并动态更新的方法

    本文实例讲述了Android实现Listview异步加载网络图片并动态更新的方法.分享给大家供大家参考,具体如下: 应用实例:解析后台返回的数据,把每条都显示在ListView中,包括活动图片.店名.活动详情.地址.电话和距离等. 在布局文件中ListView的定义: <ListView android:id="@id/maplistview" android:background="@drawable/bg" android:layout_width=&qu

  • Android实现listview动态加载数据分页的两种方法

    在android开发中,经常需要使用数据分页,比如要实现一个新闻列表的显示,或者博文列表的显示,不可能第一次加载就展示出全部,这就需要使用分页的方法来加载数据,在android中Handler经常用来在耗时的工作中,它接收子线程发送的数据,并使用数据配合更新UI,AsyncTask是在一个线程中执行耗时操作然后把结果传给UI线程,不需要你亲自去管理线程和句柄. 一.使用Handler+线程方法 1.基础知识 Handler在android系统中,主要负责发送和接收消息,它的用途主要有以下两种:

  • Android listview动态加载列表项实现代码

    最近了一个动态加载listview类表项的列子,分享出来大家学习学习,说说这个例子的实现过程,首先限定每次加载的列表项数据为10条数据,当拖动listview滚动到最后一条数据的时候再加载10条,并在Listview下方显示加载提示. 下面是我的java源码: private void showContent() { listView = (ListView) findViewById(R.id.journals_list_one); loadData(); adapter = new MyLi

  • android动态加载布局文件示例

    一.布局文件part.xml: 复制代码 代码如下: <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="

  • Android中利用动态加载实现手机淘宝的节日特效

    相信去年圣诞节打开过手机淘宝的童鞋都会对当时的特效记忆犹新吧:全屏飘雪,旁边还有个小雪人来控制八音盒背景音乐的播放,让人有种身临其境的感觉,甚至忍不住想狠狠购物了呢(误),大概就是下面这个样子滴: 嗯,确实很炫,那么我们一步步去分析是如何实现的: 一.实现下雪的 View 首先,最上面一层的全屏雪花极有可能是一个顶层的View,而这个View是通过动态加载去控制显示的(不更新淘宝也能看到这个效果).那么我们先得实现雪花效果的 View,人生苦短,拿来就用.打开 gank.io,搜索"雪花&quo

  • Android中的动态加载机制的学习研究

    在目前的软硬件环境下,Native App与Web App在用户体验上有着明显的优势,但在实际项目中有些会因为业务的频繁变更而频繁的升级客户端,造成较差的用户体验,而这也恰恰是Web App的优势.本文对网上Android动态加载jar的资料进行梳理和实践在这里与大家一起分享,试图改善频繁升级这一弊病. Android应用开发在一般情况下,常规的开发方式和代码架构就能满足我们的普通需求.但是有些特殊问题,常常引发我们进一步的沉思.我们从沉思中产生顿悟,从而产生新的技术形式. 如何开发一个可以自定

  • Android开发中Listview动态加载数据的方法示例

    本文实例讲述了Android开发中Listview动态加载数据的方法.分享给大家供大家参考,具体如下: 最近在研究网络数据加载的问题,比如我有几百,甚至上千条数据,这些数据如果一次性全部加载到arraylist,然后再加载到Listview中.我们必然会去单独开线程来做,这样造成的结果就是会出现等待时间很长,用户体验非常不好.我的想法是动态加载数据,第一次加载十条,然后往下面滑动的时候再追加十条,再往下面滑动的时候再去追加,这样大大减少了用户等待的时间,同时给处理数据留下了时间.网上看到了这样一

  • Android中ListView异步加载图片错位、重复、闪烁问题分析及解决方案

    Android ListView异步加载图片错位.重复.闪烁分析以及解决方案,具体问题分析以及解决方案请看下文. 我们在使用ListView异步加载图片的时候,在快速滑动或者网络不好的情况下,会出现图片错位.重复.闪烁等问题,其实这些问题总结起来就是一个问题,我们需要对这些问题进行ListView的优化. 比如ListView上有100个Item,一屏只显示10个Item,我们知道getView()中convertView是用来复用View对象的,因为一个Item的对应一个View对象,而Ima

  • Android中替换WebView加载网页失败时的页面

    我们用webView去请求一个网页链接的时候,如果请求网页失败或无网络的情况下,它会返回给我们这样一个页面,如下图所示: 上面这个页面就是系统自带的页面,你觉得是不是很丑?反正小编本人觉得非常丑,很难看,于是乎小编就在想能不能自定义一个页面,当数据请求失败时让系统来加载我们自定义好的页面?上网查了很多资料,都没有关于这个问题的解决方法(反正我是没有找到),经过小编的不断琢磨,今天终于实现了这个功能.以下就是本人自定义实现的数据加载失败时的页面: 这样看起来是不是觉得很高大尚.这和我们真正拿到数据

  • Android中使用TextView实现高仿京东淘宝各种倒计时效果

    今天给大家带来的是仅仅使用一个TextView实现一个高仿京东.淘宝.唯品会等各种电商APP的活动倒计时.最近公司一直加班也没来得及时间去整理,今天难得休息想把这个分享给大家,只求共同学习,以及自己后续的复习.为什么会想到使用一个TextView来实现呢?因为最近公司在做一些优化的工作,其中就有一个倒计时样式,原来开发的这个控件的同事使用了多个TextView拼接在一起的,实现的代码冗余比较大,故此项目经理就说:小宏这个就交给你来优化了,并且还要保证有一定的扩展性,当时就懵逼了.不知道从何处开始

  • Android将Glide动态加载不同大小的图片切圆角与圆形的方法

    Glide加载动态图片 首先我们先要去依赖一个githup:bumptech:glide:glide:3.7.0包: 使用Glide结合列表的样式进行图片加载: 1) 如果使用的是ListView,可以直接在Adapter的getView方法中使用: @Override public View getView(int position, View convertView, ViewGroup parent) { if (null == convertView) { //..... } Glide

  • Android中Fragment的加载方式与数据通信详解

    一.加载方式 1. 静态加载 1.1 加载步骤 (1) 创建fragment:创建自定义Fragment类继承自Fragment类,同时将自定义Fragment类与Fragment视图绑定(将layout转换成View) View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) inflater用于绑定Fragment的布局文件,同时将该布局转换成View对象并返回:con

  • Android中ListView分页加载数据功能实现

    熟悉Android的朋友们都知道,不管是微博客户端还是新闻客户端,都离不开列表组件,可以说列表组件是Android数据展现方面最重要的组件,我们今天就要讲一讲列表组件ListView加载数据的相关内容.通常来说,一个应用在展现大量数据时,不会将全部的可用数据都呈现给用户,因为这不管对于服务端还是客户端来说都是不小的压力,因此,很多应用都是采用分批次加载的形式来获取用户所需的数据.比如:微博客户端可能会在用户滑动至列表底端时自动加载下一页数据,也可能在底部放置一个"加载更多"按钮,用户点

随机推荐