原文:Android零基础入门第54节:视图切换组件ViewSwitcher

前面三期学习了ProgressBar系列组件,那本期开始一起来学习ViewAnimator组件。

一、ViewAnimator组件概述

ViewAnimator是一个基类,它继承了 FrameLayout,因此它表现出FrameLayout的特征,可以将多个View组件叠在一起。 ViewAnimator额外增加的功能正如它的名字所暗示的一样,ViewAnimator可以在View切换时表现出动画效果。

ViewAnimator及其子类的继承关系图如下图所示。

ViewAnimator及其子类也是一组非常重要的UI组件,这种组件的主要功能是增加动画效果,从而使界面更加炫。使用ViewAnimator 时可以指定如下常见XML属性。

  • android:animateFirstView:设置ViewAnimator显示第一个View组件时是否使用动画。

  • android:inAnimation:设置ViewAnimator显示组件时所使用的动画。

  • android:outAnimation:设置ViewAnimator隐藏组件时所使用的动画。

在实际项目中往往会使用ViewAnimator的几个子类。

二、ViewSwitcher使用

ViewSwitcher代表了视图切换组件,它本身继承了 FrameLayout,因此可以将多个View 层叠在一起,每次只显示一个组件。当程序控制从一个View切换到另一个View时, ViewSwitcher支持指定动画效果。

为了给ViewSwitcher添加多个组件,一般通过调用ViewSwitcher的setFactory (ViewSwitcherViewFactory)方法为之设置 ViewFactory,并由该 ViewFactory 为之创建 View 即可。

接下来通过一个简单的示例程序来学习ViewSwitcher的使用,主要是实现Android 5.0的Launcher界面的分屏、左右滚动效果。

继续使用WidgetSample工程的advancedviewsample模块,在app/main/res/layout/目录下创建viewswitcher_layout.xml文件,在其中填充如下代码片段:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 定义一个ViewSwitcher组件 -->
<ViewSwitcher
android:id="@+id/viewSwitcher"
android:layout_width="match_parent"
android:layout_height="match_parent" /> <!-- 定义滚动到上一屏的按钮 -->
<Button
android:id="@+id/prev_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:text="&lt;" />
<!-- 定义滚动到下一屏的按钮 -->
<Button
android:id="@+id/next_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:text="&gt;" />
</RelativeLayout>

上面的界面布局文件中只是定义了一个ViewSwitcher组件和两个按钮,这两个按钮分别用于控制该ViewSwitcher显示上一屏、下一屏的程序列表。

Launcher界面由GridView完成,创建布局文件slide_gridview.xml,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:numColumns="4"
android:layout_height="match_parent">
</GridView>

创建GridView中每个Item的布局文件slide_gridview_item.xml,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<!-- 定义一个垂直的LinearLayout,该容器中放置一个ImageView和一个TextView -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:padding="10dp"> <ImageView
android:id="@+id/icon_img"
android:layout_width="48dp"
android:layout_height="48dp" /> <TextView
android:id="@+id/name_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center" />
</LinearLayout>

为了方便操作,将GridView内使用的内容定义为实体类ViewSwitcherItemData,代码如下:

package com.jinyu.cqkxzsxy.android.advancedviewsample.entity;

/**
* @创建者 鑫鱻
* @描述 Android零基础入门到精通系列教程,欢迎关注微信公众号ShareExpert
*/
public class ViewSwitcherItemData {
private String name; // 应用程序名称
private int icon; // 应用程序图标 public ViewSwitcherItemData(String name, int icon) {
this.name = name;
this.icon = icon;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getIcon() {
return icon;
} public void setIcon(int icon) {
this.icon = icon;
}
}

接下来创建GridView的适配器ViewSwitcherBaseAdapter,继承BaseAdapter类,代码如下:

package com.jinyu.cqkxzsxy.android.advancedviewsample.adapter;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView; import com.jinyu.cqkxzsxy.android.advancedviewsample.R;
import com.jinyu.cqkxzsxy.android.advancedviewsample.ViewSwitcherActivity;
import com.jinyu.cqkxzsxy.android.advancedviewsample.entity.ViewSwitcherItemData; import java.util.List; /**
* @创建者 鑫鱻
* @描述 Android零基础入门到精通系列教程,欢迎关注微信公众号ShareExpert
*/
public class ViewSwitcherBaseAdapter extends BaseAdapter {
private Context mContext = null;
private List<ViewSwitcherItemData> mItemDatas = null; public ViewSwitcherBaseAdapter(Context context, List<ViewSwitcherItemData> itemDatas) {
this.mContext = context;
this.mItemDatas = itemDatas;
} @Override
public int getCount() {
// 如果已经到了最后一屏,且应用程序的数量不能整除NUMBER_PER_SCREEN
if (ViewSwitcherActivity.screenNo == ViewSwitcherActivity.screenCount - 1
&& mItemDatas.size() % ViewSwitcherActivity.NUMBER_PER_SCREEN != 0) {
// 最后一屏显示的程序数为应用程序的数量对NUMBER_PER_SCREEN求余
return mItemDatas.size() % ViewSwitcherActivity.NUMBER_PER_SCREEN;
}
// 否则每屏显示的程序数量为NUMBER_PER_SCREEN
return ViewSwitcherActivity.NUMBER_PER_SCREEN;
} @Override
public ViewSwitcherItemData getItem(int position) {
// 根据screenNo计算第position个列表项的数据
return mItemDatas.get(
ViewSwitcherActivity.screenNo * ViewSwitcherActivity.NUMBER_PER_SCREEN + position);
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (null == convertView) {
// 加载R.layout.item布局文件
convertView = LayoutInflater.from(mContext).inflate(R.layout.slide_gridview_item, null);
holder = new ViewHolder();
holder.iconImg = (ImageView)convertView.findViewById(R.id.icon_img);
holder.nameTv = (TextView)convertView.findViewById(R.id.name_tv);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
} holder.iconImg.setImageResource(getItem(position).getIcon());
holder.nameTv.setText(getItem(position).getName());
return convertView;
} private class ViewHolder{
ImageView iconImg;
TextView nameTv;
}
}

使用扩展BaseAdapter的方式为GridView提供Adapter,关键就是根据用户单击的按钮来动态计算该BaseAdapter应该显示哪些程序列表。

使用Activity类的screenNo保存当前正在显示第几屏的程序列表,BaseAdapter会根据screenNo 动态计算该Adapter总共包含多少个列表项(如getCount()方法所示),会根据screenNo计算每个列表项的数据(如getltem(int position)方法所示)。

新建ViewSwitcherActivity.java文件,加载上面新建的布局文件,具体代码如下:

package com.jinyu.cqkxzsxy.android.advancedviewsample;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.GridView;
import android.widget.ViewSwitcher; import com.jinyu.cqkxzsxy.android.advancedviewsample.adapter.ViewSwitcherBaseAdapter;
import com.jinyu.cqkxzsxy.android.advancedviewsample.entity.ViewSwitcherItemData; import java.util.ArrayList;
import java.util.List; /**
* @创建者 鑫鱻
* @描述 Android零基础入门到精通系列教程,欢迎关注微信公众号ShareExpert
*/
public class ViewSwitcherActivity extends AppCompatActivity implements View.OnClickListener {
// 定义一个常量,用于显示每屏显示的应用程序数
public static final int NUMBER_PER_SCREEN = 12;
// 记录当前正在显示第几屏的程序
public static int screenNo = -1;
// 保存程序所占的总屏数
public static int screenCount; private ViewSwitcher mViewSwitcher = null;
private Button mPrevBtn = null;
private Button mNextBtn = null;
private ViewSwitcherBaseAdapter mAdapter = null;
// 保存系统所有应用程序的List集合
private List<ViewSwitcherItemData> mItemDatas = new ArrayList<ViewSwitcherItemData>(); @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.viewswitcher_layout); // 获取界面组件
mViewSwitcher = (ViewSwitcher) findViewById(R.id.viewSwitcher);
mPrevBtn = (Button) findViewById(R.id.prev_btn);
mNextBtn = (Button) findViewById(R.id.next_btn); mViewSwitcher.setFactory(new ViewSwitcher.ViewFactory() {
// 实际上就是返回一个GridView组件
@Override
public View makeView() {
// 加载R.layout.slide_gridview组件,实际上就是一个GridView组件
return ViewSwitcherActivity.this.getLayoutInflater()
.inflate(R.layout.slide_gridview, null);
}
}); // 创建一个包含40个元素的List集合,用于模拟包含40个应用程序
for (int i = 0; i < 40; i++) {
ViewSwitcherItemData item =
new ViewSwitcherItemData("item" + i, R.mipmap.ic_launcher);
mItemDatas.add(item);
} // 计算应用程序所占的总屏数
// 如果应用程序的数量能整除NUMBER_PER_SCREEN,除法的结果就是总屏数
// 如果不能整除,总屏数应该是除法的结果再加1
screenCount = mItemDatas.size() % NUMBER_PER_SCREEN == 0 ?
mItemDatas.size() / NUMBER_PER_SCREEN :
mItemDatas.size() / NUMBER_PER_SCREEN + 1; mAdapter = new ViewSwitcherBaseAdapter(this, mItemDatas); mPrevBtn.setOnClickListener(this);
mNextBtn.setOnClickListener(this); // 页面加载时先显示第一屏
next();
} @Override
public void onClick(View view) {
switch (view.getId()){
case R.id.prev_btn:
prev();
break;
case R.id.next_btn:
next();
break;
default:
break;
}
} private void next() {
if (screenNo < screenCount - 1) {
screenNo++;
// 为ViewSwitcher的组件显示过程设置动画
mViewSwitcher.setInAnimation(this, R.anim.slide_in_right);
// 为ViewSwitcher的组件隐藏过程设置动画
mViewSwitcher.setOutAnimation(this, R.anim.slide_out_left);
// 控制下一屏将要显示的GridView对应的Adapter
((GridView) mViewSwitcher.getNextView()).setAdapter(mAdapter);
// 单击右边按钮,显示下一屏
// 学习手势检测后,也可通过手势检测实现显示下一屏
mViewSwitcher.showNext();
}
} private void prev() {
if (screenNo > 0) {
screenNo--;
// 为ViewSwitcher的组件显示过程设置动画
mViewSwitcher.setInAnimation(this, android.R.anim.slide_in_left);
// 为ViewSwitcher的组件隐藏过程设置动画
mViewSwitcher.setOutAnimation(this, android.R.anim.slide_out_right);
// 控制下一屏将要显示的GridView对应的 Adapter
((GridView) mViewSwitcher.getNextView()).setAdapter(mAdapter);
// 单击左边按钮,显示上一屏,当然可以采用手势
// 学习手势检测后,也可通过手势检测实现显示上一屏
mViewSwitcher.showPrevious();
}
}
}

重点在于为ViewSwitcher设置ViewFactory对象,并且当用户单击“<”和“>”两个按钮时控制ViewSwitcher显示“上一屏”和“下一屏”的应用程序。

当用户单击按钮时,程序的事件处理方法将会控制ViewSwitcher调用showNext() 方法显示下一屏的程序列表。而且此时screenNo被加1,因而Adapter将会动态计算下一屏的程序列表,再将该Adapter传给ViewSwitcher接下来要显示的GridView。

运行程序,点击“<”和“>”两个按钮时可以看到下图所示界面切换效果。

为了实现ViewSwitcher切换View时的动画效果,程序的事件处理方法中调用了 ViewSwitcher的setInAnimation()、setOutAnimation()方法来设置动画效果。本程序由于 Android系统只提供的两个slide_in_left、slide_out_right动画资源,需要自行提供slide_in_right、slide_out_left动画资源,详细会在Android高级部分进行讲解。

其中R.anim.slide_in_right动画资源对应的代码如下:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 设置从右边拖进来的动画
android:duration指定动画持续时间 -->
<translate
android:fromXDelta="100%p"
android:toXDelta="0"
android:duration="@android:integer/config_mediumAnimTime" />
</set>

R.anim.slide_out_left动画资源对应的代码如下:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 设置从左边拖出去的动画
android:duration指定动画持续时间 -->
<translate
android:fromXDelta="0"
android:toXDelta="-100%p"
android:duration="@android:integer/config_mediumAnimTime" />
</set>

至此,关于ViewSwitcher视图切换组件学习完毕,如果还有不清楚的地方建议回头再多做练习。

今天就先到这里,如果有问题欢迎留言一起探讨,也欢迎加入Android零基础入门技术讨论微信群,共同成长!

此文章版权为微信公众号分享达人秀(ShareExpert)——鑫鱻所有,若需转载请联系作者授权,特此声明!

往期总结分享:

Android零基础入门第1节:Android的前世今生

Android零基础入门第2节:Android 系统架构和应用组件那些事

Android零基础入门第3节:带你一起来聊一聊Android开发环境

Android零基础入门第4节:正确安装和配置JDK, 高富帅养成第一招

Android零基础入门第5节:善用ADT Bundle, 轻松邂逅女神

Android零基础入门第6节:配置优化SDK Manager, 正式约会女神

Android零基础入门第7节:搞定Android模拟器,开启甜蜜之旅

Android零基础入门第8节:HelloWorld,我的第一趟旅程出发点

Android零基础入门第9节:Android应用实战,不懂代码也可以开发

Android零基础入门第10节:开发IDE大升级,终于迎来了Android Studio

Android零基础入门第11节:简单几步带你飞,运行Android Studio工程

Android零基础入门第12节:熟悉Android Studio界面,开始装逼卖萌

Android零基础入门第13节:Android Studio配置优化,打造开发利器

Android零基础入门第14节:使用高速Genymotion,跨入火箭时代

Android零基础入门第15节:掌握Android Studio项目结构,扬帆起航

Android零基础入门第16节:Android用户界面开发概述

Android零基础入门第17节:文本框TextView

Android零基础入门第18节:输入框EditText

Android零基础入门第19节:按钮Button

Android零基础入门第20节:复选框CheckBox和单选按钮RadioButton

Android零基础入门第21节:开关组件ToggleButton和Switch

Android零基础入门第22节:图像视图ImageView

Android零基础入门第23节:图像按钮ImageButton和缩放按钮ZoomButton

Android零基础入门第24节:自定义View简单使用,打造属于你的控件

Android零基础入门第25节:简单且最常用的LinearLayout线性布局

Android零基础入门第26节:两种对齐方式,layout_gravity和gravity大不同

Android零基础入门第27节:正确使用padding和margin

Android零基础入门第28节:轻松掌握RelativeLayout相对布局

Android零基础入门第29节:善用TableLayout表格布局

Android零基础入门第30节:两分钟掌握FrameLayout帧布局

Android零基础入门第31节:少用的AbsoluteLayout绝对布局

Android零基础入门第32节:新推出的GridLayout网格布局

Android零基础入门第33节:Android事件处理概述

Android零基础入门第34节:Android中基于监听的事件处理

Android零基础入门第35节:Android中基于回调的事件处理

Android零基础入门第36节:Android系统事件的处理

Android零基础入门第37节:初识ListView

Android零基础入门第38节:初识Adapter

Android零基础入门第39节:ListActivity和自定义列表项

Android零基础入门第40节:自定义ArrayAdapter

Android零基础入门第41节:使用SimpleAdapter

Android零基础入门第42节:自定义BaseAdapter

Android零基础入门第43节:ListView优化和列表首尾使用

Android零基础入门第44节:ListView数据动态更新

Android零基础入门第45节:网格视图GridView

Android零基础入门第46节:列表选项框Spinner

Android零基础入门第47节:自动完成文本框AutoCompleteTextView

Android零基础入门第48节:可折叠列表ExpandableListView

Android零基础入门第49节:AdapterViewFlipper图片轮播

Android零基础入门第50节:StackView卡片堆叠

Android零基础入门第51节:进度条ProgressBar

Android零基础入门第52节:自定义ProgressBar炫酷进度条

Android零基础入门第53节:拖动条SeekBar和星级评分条RatingBar

Android零基础入门第54节:视图切换组件ViewSwitcher的更多相关文章

  1. Android零基础入门第78节:四大组件的纽带——Intent

    前面学习Activity时己经多次使用了 Intent,当一个Activity需要启动另一个Activity时, 程序并没有直接告诉系统要启动哪个Activity,而是通过Intent来表达自己的意图 ...

  2. Android零基础入门第56节:翻转视图ViewFlipper打造引导页和轮播图

    原文:Android零基础入门第56节:翻转视图ViewFlipper打造引导页和轮播图 前面两期学习了 ViewAnimator及其子类ViewSwitcher的使用,以及ViewSwitcher的 ...

  3. Android零基础入门第61节:滚动视图ScrollView

    原文:Android零基础入门第61节:滚动视图ScrollView 前面几期学习了ProgressBar系列组件.ViewAnimator系列组件.Picker系列组件和时间日期系列组件,接下来几期 ...

  4. Android零基础入门第60节:日历视图CalendarView和定时器Chronometer

    原文:Android零基础入门第60节:日历视图CalendarView和定时器Chronometer 上一期学习了AnalogClock.DigitalClock和TextClock时钟组件,本期继 ...

  5. Android零基础入门第68节:完善RecyclerView,添加首尾视图

    在之前学习ListView的时候,有学习过如何给ListView添加列表头和列表尾.但是通过近几期的学习,发现RecyclerView是一个比ListView更加强大和灵活的组件,今天一起来学习如何给 ...

  6. Android零基础入门第58节:数值选择器NumberPicker

    原文:Android零基础入门第58节:数值选择器NumberPicker 上一期学习了日期选择器DatePicker和时间选择器TimePicker,是不是感觉非常简单,本期继续来学习数值选择器Nu ...

  7. Android零基础入门第59节:AnalogClock、DigitalClock和TextClock时钟组件

    原文:Android零基础入门第59节:AnalogClock.DigitalClock和TextClock时钟组件 在前面一期,我们学习了DatePicker和TimePicker,在实际开发中其不 ...

  8. Android零基础入门第57节:日期选择器DatePicker和时间选择器TimePicker

    原文:Android零基础入门第57节:日期选择器DatePicker和时间选择器TimePicker 在实际开发中,经常会遇见一些时间选择器.日期选择器.数字选择器等需求,那么从本期开始来学习And ...

  9. Android零基础入门第55节:ImageSwitcher和TextSwitcher使用

    原文:Android零基础入门第55节:ImageSwitcher和TextSwitcher使用 上一期我们了解了ViewAnimator组件和ViewSwitcher组件的使用,你都掌握了吗?本期一 ...

随机推荐

  1. 【codeforces 602E】Kleofáš and the n-thlon

    time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...

  2. 【codeforces 546A】Soldier and Bananas

    time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...

  3. 毕设二:python 爬取京东的商品评论

    # -*- coding: utf-8 -*- # @author: Tele # @Time : 2019/04/14 下午 3:48 # 多线程版 import time import reque ...

  4. 【t011】最小覆盖子串

    Time Limit: 1 second Memory Limit: 32 MB [问题描述] 给定一个含有N个元素的序列A,你的任务就是求出序列A的最小覆盖子串的长度. 本题中的一些定义: 串S,是 ...

  5. Vue挂载元素的替换

    Vue根组件已有挂载DOM'#app',在render又引进一个组件,该组件最外层也是用了'#app',为何根组件的DOM'#app'会被替换掉. //main.js import Vue from ...

  6. 读取和修改xml文件

    如有一个xml文件DownData.xml,内容如下 <?xml version="1.0" standalone="yes"?> <Root ...

  7. 使用Visual Studio 2010 一步一步创建Powershell Module 和 Cmdlet

    现在就用C#写一个超级简单的Module和Cmdlet 1. 在VS中创建一个Library的项目 文件->新建->项目->C#->Class Library 在这里给我们的C ...

  8. win10 uwp 使用 asp dotnet core 做图床服务器客户端

    原文 win10 uwp 使用 asp dotnet core 做图床服务器客户端 本文告诉大家如何在 UWP 做客户端和 asp dotnet core 做服务器端来做一个图床工具   服务器端 从 ...

  9. Python经常使用内置函数介绍【filter,map,reduce,apply,zip】

    Python是一门非常简洁,非常优雅的语言,其非常多内置函数结合起来使用,能够使用非常少的代码来实现非常多复杂的功能,假设相同的功能要让C/C++/Java来实现的话,可能会头大,事实上Python是 ...

  10. Win10局域网内无法共享的解决方法分享(开启Server和ComputerBrowser服务,其它文章也不错)

    局域网共享是办公环境下经常使用的,而有些用户反应在升级到win10系统后,网络总是无法共享,给用户带来了很大的困扰,如果你也遇上这样的情况,并通过一些方法无较后,不妨尝试下小编提供的方法. 如果在Wi ...