文章目录

1.项目简介

综合之前学习过的Android知识,在这一系列的博客中,我们想要制作一个名为《智慧北京》的新闻客户端App。

接下来,将介绍与该项目相关的内容。

2.公司角色

一般来说,制作一个项目需要以下几个角色:

  • 项目经理
  • 原型图
  • 需求文档
  • 开发人员
  • Android开发工程师
  • IOS开发工程师
  • 服务器端:提供接口文档
  • 美工
  • UI设计师
  • 视觉设计师
  • 切图设计师
  • 效果图设计师
  • 测试
  • 运营
  • 销售
  • 推广
  • 打广告
  • 写软文

当然,这里我们主要扮演的是Android开发工程师。

3.开发流程

确定了角色之后,接下来就是根据开发流程来进行开发:

  • 需求分析(假设开发一个类“微信”的应用)
  • 聊天
  • 语音聊天
  • 文字聊天
  • 视频聊天
  • 朋友圈
  • 发状态
  • 评论
  • 点赞
  • 摇一摇
  • 漂流瓶
  • 产品设计
  • 原型图
  • 需求文档
  • 需求评估
  • 开发开始
  • 燃尽图
  • 提交测试
  • 第一轮测试
  • 回归测试
  • 第二轮测试
  • Bug级别
  • A:严重
  • B:重要
  • C:一般
  • D:微小
  • 发布上线
  • 将apk打包
  • 提交到应用市场
  • 迭代开发

4.环境搭建

从这一步开始,我们正式开始该项目的制作。

首先,下载相应资料:https://pan.baidu.com/s/1Oh_yqAVF0JAB48NxvXXCeQ 提取码:gchv

创建一个新项目,将主活动名改为SplashActivity,然后继续下面的步骤。

5.闪屏页面

一般来说,App应用一打开就会出现一个闪屏的页面,这里我们也来实现一下。

  1. 在res/drawable中添加红框内的图片,作为闪屏页面的背景和图片,如图所示:

Android开发实战《智慧北京》——1.页面的大体制作_xml

  1. 修改activity_splash.xml,制作闪屏页面,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/splash_bg_newyear"
android:id="@+id/rl_root">

<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@drawable/splash_horse_newyear" />
</RelativeLayout>
  1. 修改Mainifest.xml,将闪屏页面变为全屏页面,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.zhbj">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".SplashActivity"
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
  1. 修改SplashActivity,获取到根布局的实例,并增加三个动画,让根布局去执行这些动画,代码如下:
package com.example.zhbj;

import android.app.Activity;
import android.os.Bundle;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.RotateAnimation;
import android.view.animation.ScaleAnimation;
import android.widget.RelativeLayout;

public class SplashActivity extends Activity {

/**
* 闪屏页面的根布局
*/
private RelativeLayout rlRoot;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);

rlRoot = (RelativeLayout) findViewById(R.id.rl_root);

// 旋转
RotateAnimation rotateAnimation = new RotateAnimation(0,360,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f); // 基于自身中心点旋转360度
rotateAnimation.setDuration(1000); // 动画时间
rotateAnimation.setFillAfter(true); // 保持住动画结束的状态

// 缩放
ScaleAnimation scaleAnimation = new ScaleAnimation(0,1,0,1,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
scaleAnimation.setDuration(1000);
scaleAnimation.setFillAfter(true);

// 渐变
AlphaAnimation alphaAnimation = new AlphaAnimation(0,1);
alphaAnimation.setDuration(2000);
alphaAnimation.setFillAfter(true);

// 动画集合
AnimationSet animationSet = new AnimationSet(false);
animationSet.addAnimation(rotateAnimation);
animationSet.addAnimation(scaleAnimation);
animationSet.addAnimation(alphaAnimation);

// 启动动画
rlRoot.startAnimation(animationSet);
}
}
  1. 在res/drawable中添加红框内的图片,作为图标,如图所示:

Android开发实战《智慧北京》——1.页面的大体制作_android_02

  1. 修改Manifest.xml,修改图标和名称,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.zhbj">

<application
android:allowBackup="true"
android:icon="@drawable/icon_150"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".SplashActivity"
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>

6.引导页面

当通过闪屏页面时,第一次打开应用程序时通常都会弹出一个引导页面,而下一次打开就不会再跳转到引导页面了。这里我们就来实现一下。

  1. 新建一个名为GuideActivity的活动,作为引导页面
  2. 修改SplashActivity,为动画设置监听,代码如下:
package com.example.zhbj;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.RotateAnimation;
import android.view.animation.ScaleAnimation;
import android.widget.RelativeLayout;

public class SplashActivity extends Activity {

/**
* 闪屏页面的根布局
*/
private RelativeLayout rlRoot;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);

rlRoot = (RelativeLayout) findViewById(R.id.rl_root);

// 旋转
RotateAnimation rotateAnimation = new RotateAnimation(0,360,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f); // 基于自身中心点旋转360度
rotateAnimation.setDuration(1000); // 动画时间
rotateAnimation.setFillAfter(true); // 保持住动画结束的状态

// 缩放
ScaleAnimation scaleAnimation = new ScaleAnimation(0,1,0,1,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
scaleAnimation.setDuration(1000);
scaleAnimation.setFillAfter(true);

// 渐变
AlphaAnimation alphaAnimation = new AlphaAnimation(0,1);
alphaAnimation.setDuration(2000);
alphaAnimation.setFillAfter(true);

// 动画集合
AnimationSet animationSet = new AnimationSet(false);
animationSet.addAnimation(rotateAnimation);
animationSet.addAnimation(scaleAnimation);
animationSet.addAnimation(alphaAnimation);

// 启动动画
rlRoot.startAnimation(animationSet);

// 设置监听
animationSet.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {

}

@Override
public void onAnimationEnd(Animation animation) {
// 动画结束后,跳转到引导页
startActivity(new Intent(getApplicationContext(),GuideActivity.class));
finish();
}

@Override
public void onAnimationRepeat(Animation animation) {

}
});
}
}
  1. 修改activity_guide.xml,使用ViewPaper组件,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v4.view.ViewPager
android:id="@+id/vp_guide"
android:layout_width="match_parent"
android:layout_height="match_parent"/>


</RelativeLayout>
  1. 在res/drawable下增加红框内的图片,表示引导页面的样式,如图所示:

Android开发实战《智慧北京》——1.页面的大体制作_ide_03


5. 修改GuideActivity,获取ViewPaper的实例,并设定其适配器的内部类,代码如下:

package com.example.zhbj;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

import java.util.ArrayList;

public class GuideActivity extends AppCompatActivity {

/**
* ViewPaper的实例
*/
private ViewPager mViewPaper;

/**
* 引导页面的图片id数组
*/
private int[] mImageIds = new int[]{R.drawable.guide_1,R.drawable.guide_2,R.drawable.guide_3};

/**
* 存储引导页面图片的集合
*/
private ArrayList<ImageView> mImageViews;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE); // 从代码中去掉标题栏,必须在setContentView之前调用
setContentView(R.layout.activity_guide);
initViews();
initData();
}

/**
* 初始化布局
*/
private void initViews() {
mViewPaper = (ViewPager) findViewById(R.id.vp_guide);
}

/**
* 初始化数据
*/
private void initData(){
// 初始化3张图片的ImageView
mImageViews = new ArrayList<>();
for (int i = 0; i < mImageIds.length; i++) {
ImageView view = new ImageView(this);
view.setBackgroundResource(mImageIds[i]);
mImageViews.add(view);
}
mViewPaper.setAdapter(new GuideAdapter());
}

/**
* ViewPager的适配器内部类
*/
class GuideAdapter extends PagerAdapter{

@Override
public int getCount() {
return mImageIds.length;
}

@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object o) {
return view == o;
}

/**
* 初始化布局
* @param container
* @param position
* @return
*/
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
ImageView view = mImageViews.get(position);
container.addView(view);
return view;
}

/**
* 销毁布局
* @param container
* @param position
* @param object
*/
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
}
}

7.引导页面上的按钮

在引导页面的最后一页上,一般会有一个“开始体验”的按钮,这里就来实现它。

  1. 修改activity_guide.xml,在布局上添加按钮,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v4.view.ViewPager
android:id="@+id/vp_guide"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

<Button
android:id="@+id/btn_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="60dp"
android:text="开始体验"/>

</RelativeLayout>
  1. 在res/drawable下增加红框内的图片,表示按钮点击前和点击后的样式,如图所示:

Android开发实战《智慧北京》——1.页面的大体制作_ide_04

  1. 在res目录下为按钮创建一个状态选择器btn_guide_selector,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

<item android:drawable="@drawable/button_red_pressed" android:state_pressed="true" />
<item android:drawable="@drawable/button_red_normal"/>

</selector>
  1. 修改activity_guide.xml,添加按钮的背景为状态选择器,并且添加一些样式,代码如下:
<Button
android:id="@+id/btn_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="60dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:textColor="#fff"
android:background="@drawable/btn_guide_selector"
android:text="开始体验"/>
  1. 为了实现在点击按钮时按钮中的文字从白色变为黑色,在res目录下新建color目录,并在color目录下新建txt_guide_selector.xml,表示颜色的选择器,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

<item android:state_pressed="true" android:color="#000" />
<item android:color="#fff" />

</selector>
  1. 修改activity_guide.xml,添加按钮的文字为状态选择器,并且添加一些样式,代码如下:
<Button
android:id="@+id/btn_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="60dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:textColor="@color/txt_guide_selector"
android:background="@drawable/btn_guide_selector"
android:text="开始体验"/>
  1. 注意:在写选择器时,要将带有android:state_pressed="true"这个属性的语句放在前面,不然选择器会无效

8.引导页面上的灰点指示器

在引导页面上,滑动图片时应该会有一个灰点指示器表明当前浏览到哪一页了,现在来实现这个逻辑。

  1. 修改activity_guide.xml,在按钮下方增加一个水平的线性布局,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v4.view.ViewPager
android:id="@+id/vp_guide"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

<Button
android:id="@+id/btn_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="60dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:textColor="@color/txt_guide_selector"
android:background="@drawable/btn_guide_selector"
android:text="开始体验"/>

<LinearLayout
android:id="@+id/ll_container"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="30dp">

</LinearLayout>

</RelativeLayout>
  1. 在res目录下新建shape_point_normal.xml文件,用于绘制通常状态下的小灰点,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">

<!--颜色-->
<solid android:color="#cccccc"/>

<!--大小-->
<size android:width="10dp" android:height="10dp"/>
</shape>
  1. 在res目录下新建shape_point_selected.xml文件,用于绘制选中状态下的小灰点,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">

<!--颜色-->
<solid android:color="#f00"/>

<!--大小-->
<size android:width="10dp" android:height="10dp"/>

</shape>
  1. 修改GuideActivity,获取灰点的实例,并且判断在第二个点之后添加左边距,代码如下:
package com.example.zhbj;

import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ImageView;
import android.widget.LinearLayout;

import java.util.ArrayList;

public class GuideActivity extends Activity {

/**
* ViewPaper的实例
*/
private ViewPager mViewPaper;

/**
* 灰点指示器的实例
*/
private LinearLayout llContainer;

/**
* 引导页面的图片id数组
*/
private int[] mImageIds = new int[]{R.drawable.guide_1,R.drawable.guide_2,R.drawable.guide_3};

/**
* 存储引导页面图片的集合
*/
private ArrayList<ImageView> mImageViews;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE); // 从代码中去掉标题栏,必须在setContentView之前调用
setContentView(R.layout.activity_guide);
initViews();
initData();
}

/**
* 初始化布局
*/
private void initViews() {
mViewPaper = (ViewPager) findViewById(R.id.vp_guide);
llContainer = (LinearLayout) findViewById(R.id.ll_container);
}

/**
* 初始化数据
*/
private void initData(){
// 初始化3张图片的ImageView
mImageViews = new ArrayList<>();
for (int i = 0; i < mImageIds.length; i++) {
ImageView view = new ImageView(this);
view.setBackgroundResource(mImageIds[i]);
mImageViews.add(view);

// 初始化小灰点
ImageView point = new ImageView(this);
point.setImageResource(R.drawable.shape_point_normal);

// 初始化布局参数
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);
if (i > 0){
params.leftMargin = 10; // 从第二个点开始设置左边距
}
point.setLayoutParams(params); // 设置布局参数
llContainer.addView(point);

}
mViewPaper.setAdapter(new GuideAdapter());
}

/**
* ViewPager的适配器内部类
*/
class GuideAdapter extends PagerAdapter{

@Override
public int getCount() {
return mImageIds.length;
}

@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object o) {
return view == o;
}

/**
* 初始化布局
* @param container
* @param position
* @return
*/
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
ImageView view = mImageViews.get(position);
container.addView(view);
return view;
}

/**
* 销毁布局
* @param container
* @param position
* @param object
*/
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
}
}
  1. 为了让小红点显示到屏幕上,修改activity_guide.xml,将线性布局放到相对布局中,同时增加一个ImageView控件,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v4.view.ViewPager
android:id="@+id/vp_guide"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

<Button
android:id="@+id/btn_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="60dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:textColor="@color/txt_guide_selector"
android:background="@drawable/btn_guide_selector"
android:text="开始体验"/>

<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="30dp">
<LinearLayout
android:id="@+id/ll_container"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</LinearLayout>
<ImageView
android:id="@+id/iv_red_point"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/shape_point_selected"/>
</RelativeLayout>

</RelativeLayout>
  1. 修改GuideActivity,获取两个小灰点之间的距离。注意:因为获取距离的方法设计到View的绘制(measure -> layout -> draw),而View的绘制过程需要在onCreate方法之后,否则获取不到值。这里调用了视图树的相关回调方法,代码如下:
package com.example.zhbj;

import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.widget.ImageView;
import android.widget.LinearLayout;

import java.util.ArrayList;

public class GuideActivity extends Activity {

/**
* ViewPaper的实例
*/
private ViewPager mViewPaper;

/**
* 灰点指示器的实例
*/
private LinearLayout llContainer;

/**
* 引导页面的图片id数组
*/
private int[] mImageIds = new int[]{R.drawable.guide_1,R.drawable.guide_2,R.drawable.guide_3};

/**
* 存储引导页面图片的集合
*/
private ArrayList<ImageView> mImageViews;

/**
* 圆点移动的距离
*/
private int mPointDis;

/**
* 小红点的实例对象
*/
private ImageView ivRedPoint;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE); // 从代码中去掉标题栏,必须在setContentView之前调用
setContentView(R.layout.activity_guide);
initViews();
initData();
}

/**
* 初始化布局
*/
private void initViews() {
mViewPaper = (ViewPager) findViewById(R.id.vp_guide);
llContainer = (LinearLayout) findViewById(R.id.ll_container);
ivRedPoint = (ImageView) findViewById(R.id.iv_red_point);
}

/**
* 初始化数据
*/
private void initData(){
// 初始化3张图片的ImageView
mImageViews = new ArrayList<>();
for (int i = 0; i < mImageIds.length; i++) {
ImageView view = new ImageView(this);
view.setBackgroundResource(mImageIds[i]);
mImageViews.add(view);

// 初始化小灰点
ImageView point = new ImageView(this);
point.setImageResource(R.drawable.shape_point_normal);

// 初始化布局参数
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);
if (i > 0){
params.leftMargin = 10; // 从第二个点开始设置左边距
}
point.setLayoutParams(params); // 设置布局参数
llContainer.addView(point);

}
mViewPaper.setAdapter(new GuideAdapter());

// 监听ViewPager滑动事件,更新小红点位置
mViewPaper.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {

// 监听滑动事件
@Override
public void onPageScrolled(int i, float v, int i1) {

}

@Override
public void onPageSelected(int i) {

}

@Override
public void onPageScrollStateChanged(int i) {

}
});

// 计算圆点移动距离 = 第二个圆点的左边距 - 第一个圆点的左边距
// measure -> layout -> draw,必须onCreate执行结束之后,才会开始绘制布局,走这三个方法
// mPointDis = llContainer.getChildAt(1).getLeft() - llContainer.getChildAt(0).getLeft();

// 监听layout执行结束的事件,一旦结束之后,再去获取当前的left的位置
// 视图树
ivRedPoint.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

// 一旦视图树的layout方法调用完成,就会回调此方法
@Override
public void onGlobalLayout() {
// 布局位置已经确定,可以拿到位置信息了
mPointDis = llContainer.getChildAt(1).getLeft() - llContainer.getChildAt(0).getLeft();

// 移除观察者
// ivRedPoint.getViewTreeObserver().removeOnGlobalLayoutListener(this);
ivRedPoint.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
}

/**
* ViewPager的适配器内部类
*/
class GuideAdapter extends PagerAdapter{

@Override
public int getCount() {
return mImageIds.length;
}

@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object o) {
return view == o;
}

/**
* 初始化布局
* @param container
* @param position
* @return
*/
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
ImageView view = mImageViews.get(position);
container.addView(view);
return view;
}

/**
* 销毁布局
* @param container
* @param position
* @param object
*/
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
}
}
  1. 修改GuideActivity,修改mViewPaper的监听方法onPageScrolled(),增加小红点的移动逻辑,代码如下:
// 监听滑动事件
@Override
public void onPageScrolled(int i, float v, int i1) {

// 通过修改小红点的左边距,来更新小红点的位置
int leftMargin = (int) (mPointDis * v + mPointDis * i + 0.5f); // 要将当前的位置信息产生的距离加进来

// 获取小红点的布局参数
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) ivRedPoint.getLayoutParams();
params.leftMargin = leftMargin;
ivRedPoint.setLayoutParams(params);
}

9.引导页面上的“开始体验”按钮

灰点指示器的逻辑完成后,我们需要完善之前第7步中增加按钮的逻辑,让ViewPaper到达第三页时才显示按钮,并添加活动跳转等逻辑,现在开始实现。

  1. 修改activity_guide.xml,让Button组件在一开始时不显示,代码如下:
<Button
android:id="@+id/btn_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="60dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:textColor="@color/txt_guide_selector"
android:background="@drawable/btn_guide_selector"
android:visibility="gone"
android:text="开始体验"/>
  1. 修改GuideActivity,获取按钮实例,并且在ViewPaper监听器的onPageSelected()方法中判断是否到达最后一页,代码如下:
package com.example.zhbj;

import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;

import java.util.ArrayList;

public class GuideActivity extends Activity {

/**
* ViewPaper的实例
*/
private ViewPager mViewPaper;

/**
* 灰点指示器的实例
*/
private LinearLayout llContainer;

/**
* 引导页面的图片id数组
*/
private int[] mImageIds = new int[]{R.drawable.guide_1,R.drawable.guide_2,R.drawable.guide_3};

/**
* 存储引导页面图片的集合
*/
private ArrayList<ImageView> mImageViews;

/**
* 圆点移动的距离
*/
private int mPointDis;

/**
* 小红点的实例对象
*/
private ImageView ivRedPoint;

/**
* “开始体验”按钮的实例对象
*/
private Button btnstart;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE); // 从代码中去掉标题栏,必须在setContentView之前调用
setContentView(R.layout.activity_guide);
initViews();
initData();
}

/**
* 初始化布局
*/
private void initViews() {
mViewPaper = (ViewPager) findViewById(R.id.vp_guide);
llContainer = (LinearLayout) findViewById(R.id.ll_container);
ivRedPoint = (ImageView) findViewById(R.id.iv_red_point);
btnstart = (Button) findViewById(R.id.btn_start);
}

/**
* 初始化数据
*/
private void initData(){
// 初始化3张图片的ImageView
mImageViews = new ArrayList<>();
for (int i = 0; i < mImageIds.length; i++) {
ImageView view = new ImageView(this);
view.setBackgroundResource(mImageIds[i]);
mImageViews.add(view);

// 初始化小灰点
ImageView point = new ImageView(this);
point.setImageResource(R.drawable.shape_point_normal);

// 初始化布局参数
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);
if (i > 0){
params.leftMargin = 10; // 从第二个点开始设置左边距
}
point.setLayoutParams(params); // 设置布局参数
llContainer.addView(point);

}
mViewPaper.setAdapter(new GuideAdapter());

// 监听ViewPager滑动事件,更新小红点位置
mViewPaper.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {

// 监听滑动事件
@Override
public void onPageScrolled(int i, float v, int i1) {

// 通过修改小红点的左边距,来更新小红点的位置
int leftMargin = (int) (mPointDis * v + mPointDis * i + 0.5f); // 要将当前的位置信息产生的距离加进来

// 获取小红点的布局参数
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) ivRedPoint.getLayoutParams();
params.leftMargin = leftMargin;
ivRedPoint.setLayoutParams(params);
}

@Override
public void onPageSelected(int i) {
if (i == mImageIds.length - 1){
// 最后一个页面显示“开始体验”按钮
btnstart.setVisibility(View.VISIBLE);
}
else {
btnstart.setVisibility(View.GONE);
}
}

@Override
public void onPageScrollStateChanged(int i) {

}
});

// 计算圆点移动距离 = 第二个圆点的左边距 - 第一个圆点的左边距
// measure -> layout -> draw,必须onCreate执行结束之后,才会开始绘制布局,走这三个方法
// mPointDis = llContainer.getChildAt(1).getLeft() - llContainer.getChildAt(0).getLeft();

// 监听layout执行结束的事件,一旦结束之后,再去获取当前的left的位置
// 视图树
ivRedPoint.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

// 一旦视图树的layout方法调用完成,就会回调此方法
@Override
public void onGlobalLayout() {
// 布局位置已经确定,可以拿到位置信息了
mPointDis = llContainer.getChildAt(1).getLeft() - llContainer.getChildAt(0).getLeft();

// 移除观察者
// ivRedPoint.getViewTreeObserver().removeOnGlobalLayoutListener(this);
ivRedPoint.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
}

/**
* ViewPager的适配器内部类
*/
class GuideAdapter extends PagerAdapter{

@Override
public int getCount() {
return mImageIds.length;
}

@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object o) {
return view == o;
}

/**
* 初始化布局
* @param container
* @param position
* @return
*/
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
ImageView view = mImageViews.get(position);
container.addView(view);
return view;
}

/**
* 销毁布局
* @param container
* @param position
* @param object
*/
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
}
}
  1. 修改GuideActivity,增加按钮的点击事件,让其跳转到主页面。同时新创建一个名为MainActivity的活动,表示主页面,代码如下:
package com.example.zhbj;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;

import java.util.ArrayList;

/**
* 引导页面
*/
public class GuideActivity extends Activity {

/**
* ViewPaper的实例
*/
private ViewPager mViewPaper;

/**
* 灰点指示器的实例
*/
private LinearLayout llContainer;

/**
* 引导页面的图片id数组
*/
private int[] mImageIds = new int[]{R.drawable.guide_1,R.drawable.guide_2,R.drawable.guide_3};

/**
* 存储引导页面图片的集合
*/
private ArrayList<ImageView> mImageViews;

/**
* 圆点移动的距离
*/
private int mPointDis;

/**
* 小红点的实例对象
*/
private ImageView ivRedPoint;

/**
* “开始体验”按钮的实例对象
*/
private Button btnstart;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE); // 从代码中去掉标题栏,必须在setContentView之前调用
setContentView(R.layout.activity_guide);
initViews();
initData();
}

/**
* 初始化布局
*/
private void initViews() {
mViewPaper = (ViewPager) findViewById(R.id.vp_guide);
llContainer = (LinearLayout) findViewById(R.id.ll_container);
ivRedPoint = (ImageView) findViewById(R.id.iv_red_point);
btnstart = (Button) findViewById(R.id.btn_start);
}

/**
* 初始化数据
*/
private void initData(){
// 初始化3张图片的ImageView
mImageViews = new ArrayList<>();
for (int i = 0; i < mImageIds.length; i++) {
ImageView view = new ImageView(this);
view.setBackgroundResource(mImageIds[i]);
mImageViews.add(view);

// 初始化小灰点
ImageView point = new ImageView(this);
point.setImageResource(R.drawable.shape_point_normal);

// 初始化布局参数
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);
if (i > 0){
params.leftMargin = 10; // 从第二个点开始设置左边距
}
point.setLayoutParams(params); // 设置布局参数
llContainer.addView(point);

}
mViewPaper.setAdapter(new GuideAdapter());

// 监听ViewPager滑动事件,更新小红点位置
mViewPaper.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {

// 监听滑动事件
@Override
public void onPageScrolled(int i, float v, int i1) {

// 通过修改小红点的左边距,来更新小红点的位置
int leftMargin = (int) (mPointDis * v + mPointDis * i + 0.5f); // 要将当前的位置信息产生的距离加进来

// 获取小红点的布局参数
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) ivRedPoint.getLayoutParams();
params.leftMargin = leftMargin;
ivRedPoint.setLayoutParams(params);
}

@Override
public void onPageSelected(int i) {
if (i == mImageIds.length - 1){
// 最后一个页面显示“开始体验”按钮
btnstart.setVisibility(View.VISIBLE);
}
else {
btnstart.setVisibility(View.GONE);
}
}

@Override
public void onPageScrollStateChanged(int i) {

}
});

// 计算圆点移动距离 = 第二个圆点的左边距 - 第一个圆点的左边距
// measure -> layout -> draw,必须onCreate执行结束之后,才会开始绘制布局,走这三个方法
// mPointDis = llContainer.getChildAt(1).getLeft() - llContainer.getChildAt(0).getLeft();

// 监听layout执行结束的事件,一旦结束之后,再去获取当前的left的位置
// 视图树
ivRedPoint.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

// 一旦视图树的layout方法调用完成,就会回调此方法
@Override
public void onGlobalLayout() {
// 布局位置已经确定,可以拿到位置信息了
mPointDis = llContainer.getChildAt(1).getLeft() - llContainer.getChildAt(0).getLeft();

// 移除观察者
// ivRedPoint.getViewTreeObserver().removeOnGlobalLayoutListener(this);
ivRedPoint.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});

// 开始按钮的点击事件
btnstart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(getApplicationContext(),MainActivity.class));
finish();
}
});
}

/**
* ViewPager的适配器内部类
*/
class GuideAdapter extends PagerAdapter{

@Override
public int getCount() {
return mImageIds.length;
}

@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object o) {
return view == o;
}

/**
* 初始化布局
* @param container
* @param position
* @return
*/
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
ImageView view = mImageViews.get(position);
container.addView(view);
return view;
}

/**
* 销毁布局
* @param container
* @param position
* @param object
*/
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
}
}
  1. 为了让用户第二次打开应用时不再出现引导页,需要使用SharePreference去存储数据。新建一个util包,并增加一个PrefUtils工具类,代码如下:
package com.example.zhbj.util;

import android.content.Context;
import android.content.SharedPreferences;

/**
* 专门访问和设置SharePreference的工具类, 保存和配置一些设置信息
*
* @author Kevin
*
*/
public class PrefUtils {

private static final String SHARE_PREFS_NAME = "itcast";

public static void putBoolean(Context ctx, String key, boolean value) {
SharedPreferences pref = ctx.getSharedPreferences(SHARE_PREFS_NAME,
Context.MODE_PRIVATE);

pref.edit().putBoolean(key, value).commit();
}

public static boolean getBoolean(Context ctx, String key,
boolean defaultValue) {
SharedPreferences pref = ctx.getSharedPreferences(SHARE_PREFS_NAME,
Context.MODE_PRIVATE);

return pref.getBoolean(key, defaultValue);
}

public static void putString(Context ctx, String key, String value) {
SharedPreferences pref = ctx.getSharedPreferences(SHARE_PREFS_NAME,
Context.MODE_PRIVATE);

pref.edit().putString(key, value).commit();
}

public static String getString(Context ctx, String key, String defaultValue) {
SharedPreferences pref = ctx.getSharedPreferences(SHARE_PREFS_NAME,
Context.MODE_PRIVATE);

return pref.getString(key, defaultValue);
}

public static void putInt(Context ctx, String key, int value) {
SharedPreferences pref = ctx.getSharedPreferences(SHARE_PREFS_NAME,
Context.MODE_PRIVATE);

pref.edit().putInt(key, value).commit();
}

public static int getInt(Context ctx, String key, int defaultValue) {
SharedPreferences pref = ctx.getSharedPreferences(SHARE_PREFS_NAME,
Context.MODE_PRIVATE);

return pref.getInt(key, defaultValue);
}

}
  1. 修改GuideActivity,修改按钮的点击事件,在点击按钮时将标志位存储到ps中,代码如下:
// 开始按钮的点击事件
btnstart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 在sp中记录访问过引导页的状态
PrefUtils.putBoolean(getApplicationContext(),"is_guide_show",true);

startActivity(new Intent(getApplicationContext(),MainActivity.class));
finish();
}
});
  1. 修改SplashActivity,修改onAnimationEnd()方法,在动画结束后进行判断,看是否是第一次进入引用,代码如下:
@Override
public void onAnimationEnd(Animation animation) {
// 判断有没有展示过引导页
boolean isGuideShow = PrefUtils.getBoolean(getApplicationContext(), "is_guide_show", false);
if (!isGuideShow){
// 动画结束后,跳转到引导页
startActivity(new Intent(getApplicationContext(),GuideActivity.class));
}else {
// 跳到主页面
startActivity(new Intent(getApplicationContext(),MainActivity.class));
}
finish();
}

10.主页面

在引导页面上点击“开始体验”按钮后,就会进入到主页面。主页面为MainActivity,布局文件为activity_main.xml,这里不再详述。

11.主页面上的侧边栏

一般来说,主页面在往右滑动时应该会在左侧出现一个菜单,显示相应的功能,并将主菜单顶到右侧,这里我们用SlidingMenu框架来实现。

  1. 导入SlidingMenu的第三方库,注意修改相关依赖,可参考下面给出的博客
    ​​​https://www.jianshu.com/p/8600fddb5a6f​
  2. 修改MainActivity,继承SlidingActivity,并将onCreate的访问修饰符改为public,代码如下:
package com.example.zhbj;

import android.os.Bundle;

import com.jeremyfeinstein.slidingmenu.lib.app.SlidingActivity;

/**
* 主页面
*/
public class MainActivity extends SlidingActivity {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// 设置侧边栏的布局
setBehindContentView(R.layout.left_menu);

// 获取当前侧边栏对象
SlidingMenu slidingMenu = getSlidingMenu();
// 设置侧边栏的触摸模式为全屏触摸
slidingMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
// 设置侧边栏为左右都有的模式,不然默认只展现一个
// slidingMenu.setMode(SlidingMenu.LEFT_RIGHT);
// 设置右侧的侧边栏
// slidingMenu.setSecondaryMenu(res);
// 设置侧边栏的宽度
slidingMenu.setBehindOffset(260);// 屏幕预留200px的宽度
}
}
  1. 添加left_menu.xml,作为侧边栏的布局文件,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#f00"
android:layout_width="match_parent"
android:layout_height="match_parent">

</LinearLayout>

12.主页面上的规划

侧边栏大概实现后,接下来要去实现主页面的内容。为了解耦,这里将侧边栏和内容分为两个不同的fragment来实现。

  1. 新建一个fragment包,在包下新建一个BaseFragment抽象类,为了上下兼容,继承的是v4的包,代码如下:
package com.example.zhbj.fragment;

import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

/**
* Fragment基类
*/
public abstract class BaseFragment extends Fragment {

/**
* Activity对象,可以当作Context去使用
*/
public Activity mActivity;

/**
* View对象,Fragment的根布局
*/
public View mRootView;

// Fragment创建
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mActivity = getActivity(); // 获取Fragment所依赖的Activity对象
}

// Fragment初始化布局
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mRootView = initView();
return mRootView;
}

// Fragment所在的activity创建完成
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initData();
}

/**
* 初始化布局,抽象方法,需要由子类来实现
*/
public abstract View initView();

/**
* 初始化数据,子类可以不实现
*/
public void initData(){

}
}
  1. 在fragment包下创建LeftMenuFragment,代表侧边栏,代码如下:
package com.example.zhbj.fragment;

import android.view.View;

import com.example.zhbj.R;

/**
* 侧边栏Fragment
*/
public class LeftMenuFragment extends BaseFragment {
@Override
public View initView() {
View view = View.inflate(mActivity, R.layout.fragment_left_menu, null);
return view;
}
}
  1. 建立LeftMenuFragment的布局文件fragment_left_menu.xml,作为侧边栏的布局文件,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f00">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="侧边栏Fragment"/>

</LinearLayout>
  1. 在fragment包下创建ContentFragment,代表内容,代码如下:
package com.example.zhbj.fragment;

import android.view.View;

import com.example.zhbj.R;

/**
* 主页面Fragment
*/
public class ContentFragment extends BaseFragment {
@Override
public View initView() {
View view = View.inflate(mActivity, R.layout.fragment_content, null);
return view;
}
}
  1. 建立ContentFragment的布局文件fragment_content.xml,作为内容的布局文件,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="主页面Fragment"/>

</LinearLayout>
  1. 修改activity_main.xml、left_menu.xml,将布局方式改为帧布局,因为要用碎片替换,留下一个空架子即可,代码分别如下:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fl_content"
android:layout_width="match_parent"
android:layout_height="match_parent">

</FrameLayout>
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fl_left_menu"
android:layout_width="match_parent"
android:layout_height="match_parent">

</FrameLayout>
  1. 修改MainActivity,添加一个initFragment()方法,替换原有的布局,使用碎片,将继承变更为SlidingFragmentActivity,代码如下:
package com.example.zhbj;

import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;

import com.example.zhbj.fragment.ContentFragment;
import com.example.zhbj.fragment.LeftMenuFragment;
import com.jeremyfeinstein.slidingmenu.lib.SlidingMenu;
import com.jeremyfeinstein.slidingmenu.lib.app.SlidingFragmentActivity;

/**
* 主页面
*
* 当一个activity要展示Fragment时,必须要继承FragmentActivity
*/
public class MainActivity extends SlidingFragmentActivity {

/**
* 定义“内容”碎片的标志
*/
private static final String TAG_CONTENT = "TAG_CONTENT";

/**
* 定义“侧边栏”碎片的标志
*/
private static final String TAG_LEFT_MENU = "TAG_LEFT_MENU";

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);

// 设置侧边栏的布局
setBehindContentView(R.layout.left_menu);

// 获取当前侧边栏对象
SlidingMenu slidingMenu = getSlidingMenu();
// 设置侧边栏的触摸模式为全屏触摸
slidingMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
// 设置侧边栏为左右都有的模式,不然默认只展现一个
// slidingMenu.setMode(SlidingMenu.LEFT_RIGHT);
// 设置右侧的侧边栏
// slidingMenu.setSecondaryMenu(res);
// 设置侧边栏的宽度
slidingMenu.setBehindOffset(260);// 屏幕预留200px的宽度
initFragment();
}

/**
* 初始化Fragment
*/
private void initFragment(){
// 获取Fragment管理器
FragmentManager fragmentManager = getSupportFragmentManager();
// FragmentManager开启一个事务
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fl_content,new ContentFragment(),TAG_CONTENT); // 使用fragment替换现有布局,第一个参数表示当前布局的id,第二个参数表示要替换的Fragment对象
fragmentTransaction.replace(R.id.fl_left_menu,new LeftMenuFragment(),TAG_LEFT_MENU); // 第三个参数为打一个标记,可以调用这个碎片对象
fragmentTransaction.commit(); // 提交事务
//fragmentManager.findFragmentByTag(TAG); // 通过Tag找到fragment对象
}
}

13.主页面上的低栏标签

要实现主页面下面的低栏标签,简单的方法可以使用RadioGroup + RadioButton来实现,而内容可以由禁用掉滑动功能的ViewPaper来实现。

  1. 在res/drawable下增加资料中的所有图片,如图所示:

Android开发实战《智慧北京》——1.页面的大体制作_xml_05

  1. 修改styles.xml,因为要添加5个RadioButton,所以可以将样式抽取成名为BottomTabStyle的样式,代码如下:
<resources>

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">"colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>

<style name="BottomTabStyle">"android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_weight">1</item>
<item name="android:button">@null</item>
<item name="android:gravity">center</item>
<item name="android:paddingTop">5dp</item>
<item name="android:paddingBottom">5dp</item>
<item name="android:drawablePadding">3dp</item>
<item name="android:background">@android:color/transparent</item>
<item name="android:textColor">@color/txt_tab_selector</item>
</style>

</resources>
  1. 为了实现按钮被选中时的状态切换,新建五个对应五种功能的状态选择器tab_home_selector.xml,tab_news_selector.xml,tab_smart_selector.xml,tab_smart_gov.xml,tab_setting_selector.xml代码分别如下:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

<item android:state_checked="true" android:drawable="@drawable/home_press"/>
<item android:drawable="@drawable/home"/>

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

<item android:state_checked="true" android:drawable="@drawable/newscenter_press"/>
<item android:drawable="@drawable/newscenter"/>

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

<item android:state_checked="true" android:drawable="@drawable/smartservice_press"/>
<item android:drawable="@drawable/smartservice"/>

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

<item android:state_checked="true" android:drawable="@drawable/govaffairs_press"/>
<item android:drawable="@drawable/govaffairs"/>

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

<item android:state_checked="true" android:drawable="@drawable/setting_press"/>
<item android:drawable="@drawable/setting"/>

</selector>
  1. 在res/color包下新建txt_tab_selector,用于表示按钮被选中时的文字颜色变化,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#f00" android:state_checked="true"/>
<item android:color="#fff"/>
</selector>
  1. 修改fragment_content.xml,添加相应布局,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v4.view.ViewPager
android:id="@+id/vp_content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>

<RadioGroup
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="@drawable/bottom_tab_bg">

<RadioButtonstyle="@style/BottomTabStyle"
android:id="@+id/rb_home"
android:text="首页"
android:checked="true"
android:drawableTop="@drawable/tab_home_selector" />

<RadioButtonstyle="@style/BottomTabStyle"
android:id="@+id/rb_news"
android:text="新闻中心"
android:drawableTop="@drawable/tab_news_selector" />

<RadioButtonstyle="@style/BottomTabStyle"
android:id="@+id/rb_smart"
android:text="智慧服务"
android:drawableTop="@drawable/tab_smart_selector" />

<RadioButtonstyle="@style/BottomTabStyle"
android:id="@+id/rb_gov"
android:text="政务"
android:drawableTop="@drawable/tab_gov_selector" />

<RadioButtonstyle="@style/BottomTabStyle"
android:id="@+id/rb_setting"
android:text="设置"
android:drawableTop="@drawable/tab_setting_selector" />
</RadioGroup>
</LinearLayout>

14.主页面上的内容

现在低栏标签已经实现,接下来就是根据不同的低栏标签呈现不同的内容了。

  1. 为了实现一个页面的基类,新建一个Base包,然后再新建一个BasePaper类,代码如下:
package com.example.zhbj.base;

/**
* 5个标签页的基类
*
* 共性:子类都有标题栏,所以可以直接在父类中加载布局页面
*/
public class BasePaper {

/**
* 初始化布局
*/
public void initView(){

}

/**
* 初始化数据
*/
public void initData(){

}
}
  1. 新建一个布局文件title_bar.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="wrap_content"
android:background="@drawable/title_red_bg">

<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:textColor="#fff"
android:textSize="25sp"
android:text="标题"/>

<ImageButton
android:id="@+id/btn_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:background="@null"
android:layout_marginLeft="10dp"
android:src="@drawable/img_menu"/>

</RelativeLayout>
  1. 新建一个布局文件base_paper,代表标签页基类的布局,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">

<include layout="@layout/title_bar"/>

<FrameLayout
android:id="@+id/fl_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">

</FrameLayout>

</LinearLayout>
  1. 修改BasePaper,完善initView()方法,代码如下:
package com.example.zhbj.base;

import android.app.Activity;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.TextView;

import com.example.zhbj.R;

/**
* 5个标签页的基类
*
* 共性:子类都有标题栏,所以可以直接在父类中加载布局页面
*/
public class BasePaper {

/**
* Activity对象
*/
public Activity mActivity;

/**
* 标题的对象
*/
public TextView tvTitle;

/**
* 标题按钮的对象
*/
public ImageButton btnMenu;

/**
* 当前页面的根布局
*/
public View mRootView;

/**
* 内容的对象
*/
public FrameLayout flContainer;

public BasePaper(Activity activity) {
mActivity = activity;
// 在页面对象创建时,就初始化了布局
mRootView = initView();
}

/**
* 初始化布局
*/
public View initView(){
View view = View.inflate(mActivity, R.layout.base_paper, null);
tvTitle = (TextView) view.findViewById(R.id.tv_title);
btnMenu = (ImageButton) view.findViewById(R.id.btn_menu);
flContainer = view.findViewById(R.id.fl_container);
return view;
}

/**
* 初始化数据
*/
public void initData(){

}
}
  1. 修改ContentFragment,完善ViewPaper,代码如下:
package com.example.zhbj.fragment;

import android.support.annotation.NonNull;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;

import com.example.zhbj.R;

/**
* 主页面Fragment
*/
public class ContentFragment extends BaseFragment {

/**
* 获取到ViewPaper的对象
*/
private ViewPager mViewPaper;
@Override
public View initView() {
View view = View.inflate(mActivity, R.layout.fragment_content, null);
mViewPaper = (ViewPager) view.findViewById(R.id.vp_content);
return view;
}

/**
* 初始化ViewPaper的数据
*/
@Override
public void initData() {
//mViewPaper.setAdapter();
}

/**
* ViewPaper的实现类
*/
class ContentAdapter extends PagerAdapter{

@Override
public int getCount() {
return 0;
}

@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object o) {
return view == o;
}

@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
return super.instantiateItem(container, position);
}

@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
}
}