跳到主要内容

凯迪拉克案例学习报告

本学习报告围绕凯迪拉克案例,点此下载案例文件

学习要点

视频播放

使用 VideoView 组件实现视频播放。

页面布局部分

  1. 创建一个 VideoView 组件
  2. 设置它的id

以下是相关代码

fragment_home.xml
...
<VideoView
android:id="@+id/vv"
android:layout_width="430dp"
android:layout_height="280dp" />
...

代码部分

  1. 创建一个名为uri的 Uri 类型变量
  2. 将工程文件中的视频资源转换成Uri类型并存入uri变量
  3. 使用VideoView组件自带的 setVideoURI(uri) 方法设置视频资源为uri变量中的内容
  4. 使用VideoView组件自带的 setOnPreparedListener 设置准备就绪的监听事件
  5. 在监听事件内使用 setLooping(true) 使视频循环播放
  6. 使用VideoView组件自带的 start() 方法让视频开始播放

以下是相关代码

HomeFragment.java
...
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);

Uri uri = Uri.parse("android.resource://" + getContext().getPackageName() + "/" + R.raw.front);
binding.vv.setVideoURI(uri); // 设置视频资源
binding.vv.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.setLooping(true); // 循环播放
}
});
binding.vv.start();
}
...

约束布局

约束布局(ConstraintLayout)是有别于线性布局(LinearLayout)的一种布局方式,它的特点就是能够使一个视图层叠于另一个视图之上。

页面布局部分

在最外层使用 androidx.constraintlayout.widget.ConstraintLayout 以使用约束布局 在约束布局中,可使用 app:layout_constraint_toOf="视图id或层级" 来调整视图相对于其他视图的位置

代码含义
app:layout_constraintTop_toTopOf该视图的顶边对齐相应视图的顶边
app:layout_constraintTop_toBottomOf该视图的顶边对齐相应视图的底边
app:layout_constraintTop_toLeftOf该视图的顶边对齐相应视图的左边
app:layout_constraintTop_toRightOf该视图的顶边对齐相应视图的右边
app:layout_constraintTop_toStartOf该视图的顶边对齐相应视图的起始边
app:layout_constraintTop_toEndOf该视图的顶边对齐相应视图的结束边
app:layout_constraintBottom_toTopOf该视图的底边对齐相应视图的顶边
......

以下是相关代码

activity_main.xml
<?xml version="1.0" encoding="utf-8"?><!--约束布局-->
<androidx.constraintlayout.widget.ConstraintLayout 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="@color/black"
tools:context=".MainActivity">

<ImageView
android:id="@+id/background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="center"
android:src="@drawable/c3"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycleview"
android:layout_width="355dp"
android:layout_height="678dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />

<!--网格布局-->
<GridLayout
android:id="@+id/gridLayout"
android:layout_width="914dp"
android:layout_height="874dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/logo_italic" />

</androidx.constraintlayout.widget.ConstraintLayout>

RecyclerView的运用

RecyclerView是一个强大的视图容器,用于显示大量数据集的列表或网格。 它是对ListView的改进和扩展,提供了更高的灵活性和性能优化的机会。 与ListView相比,RecyclerView的实现需要更多的代码。 在简单的列表显示情况下,ListView可能更加方便。 对于复杂的布局需求和更好的性能优化,RecyclerView则是更好的选择。

页面布局部分

在主界面中的使用

  1. 创建 androidx.recyclerview.widget.RecyclerView 组件
  2. 设置约束布局相关属性
  3. 设置id属性

以下是相关代码

activity_main.xml
...
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycleview"
android:layout_width="355dp"
android:layout_height="678dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
...

每个列表项的布局

  1. 在工程文件的 layout 目录中新建 rv_item.xml 布局文件
  2. 写出一个RecyclerView列表项的布局视图

以下是相关代码

rv_item.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content">

<ImageView
android:id="@+id/iv_photo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="fitXY"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/c1" />

<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="TextView"
android:textColor="@color/white"
android:textSize="20dp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@+id/iv_photo"
app:layout_constraintEnd_toEndOf="@+id/iv_photo" />
</androidx.constraintlayout.widget.ConstraintLayout>

代码部分

适配器

RecyclerView和ListView一样需要使用适配器

  1. 在工程文件中新建 MyAdapter.java 文件
  2. MyAdapter 类后继承(extends) RecyclerView.Adapter<适配器文件名.ViewHolder>
  3. 使用 Android Studio 自带的提示功能将代码引入或补全
  4. 将每一个方法中的代码补充完整

以下是相关代码

MyAdapter.java
package com.example.cadillac;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.List;

class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
//RecyclerView适配器
private Context context;//上下文
private List<Cadillac> data;//数据源
private IsClicked isClicked;//接口

//构造方法
public MyAdapter(Context context, List<Cadillac> data, IsClicked isClicked) {
this.context = context;
this.data = data;
this.isClicked = isClicked;
}

//创建并返回RecyclerView的ViewHolder对象
@NonNull
@Override
public MyAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//绑定item的XML文件
View view = LayoutInflater.from(context)
.inflate(R.layout.rv_item, parent, false);
return new ViewHolder(view);
}

//将ViewHolder中绑定组件的内容根据数据源进行赋值和修改
@Override
public void onBindViewHolder(@NonNull MyAdapter.ViewHolder holder, int position) {
holder.imageView.setImageResource(data.get(position).getPic());
holder.textView.setText(data.get(position).getName() + "");
holder.imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
isClicked.Position(position);
}
});
}

//根据数据源设置RecyclerView共有几项
@Override
public int getItemCount() {
return data.size();
}

//用于绑定item的XML文件中的各个组件
public class ViewHolder extends RecyclerView.ViewHolder {
public ImageView imageView;
public TextView textView;

public ViewHolder(@NonNull View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.iv_photo);
textView = itemView.findViewById(R.id.tv_name);
}
}
}

主界面

当然,需要使用RecyclerView除了适配器还需要在主界面中设置适配器和数据源

  1. 创建一个名为 dataList<Cadillac> 类型变量,并声明是一个新的对象数组
  2. 初始化 activity_main.xml 文件中的RecyclerView组件
  3. 使用 data.add(new Cadillac(图片资源, 车辆名称)); 的方法在数据源中添加数据
  4. 使用 RecyclerView 组件自带的 setLayoutManager(new StaggeredGridLayoutManager(行数或列数, StaggeredGridLayoutManager.横向或纵向)); 来设置瀑布流视图
  5. 使用 RecyclerView 组件自带的 setAdapter(new MyAdapter(上下文,数据源,点击事件方法)) 来设置适配器

以下是相关代码

MainActivity.java
public class MainActivity extends AppCompatActivity {
List<Cadillac> data = new ArrayList<>();//数据源
...

//组件变量
private RecyclerView rv;
...

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
//初始化组件
rv = findViewById(R.id.recycleview);
...

//在数据源中添加数据
data.add(new Cadillac(R.drawable.c1, "LYRIQ锐歌"));
data.add(new Cadillac(R.drawable.c2, "CT6"));
data.add(new Cadillac(R.drawable.c3, "XT6"));
data.add(new Cadillac(R.drawable.c4, "驾控"));
data.add(new Cadillac(R.drawable.c5, "安全"));
data.add(new Cadillac(R.drawable.c6, "设计"));
data.add(new Cadillac(R.drawable.q2, "volvo"));
data.add(new Cadillac(R.drawable.q3, "volvo"));
data.add(new Cadillac(R.drawable.q4, "volvo"));

//RecyclerView设置适配器
rv.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
rv.setAdapter(new MyAdapter(this, data, new IsClicked() {
@Override
public void Position(int position) {
if (position < 6) {
iv.setImageResource(data.get(position).getPic());
}
switch (position) {
case 0:
bundle.putString("key", "四驱高性能版 逐 光 而 来");//将要传输到相应的碎片中的数据放入容器
LYRIQFragment fragment = new LYRIQFragment();//创建对应的碎片
fragment.setArguments(bundle);//发送要传输的数据
getSupportFragmentManager().beginTransaction()
.replace(R.id.gridLayout, fragment).commit();//将当前显示的碎片替换成指定的碎片
break;
case 1:
bundle.putString("key", "豪华 不 止 于 所 见");
CT6Fragment fragment1 = new CT6Fragment();
fragment1.setArguments(bundle);
getSupportFragmentManager().beginTransaction()
.replace(R.id.gridLayout, fragment1).commit();
break;
case 2:
bundle.putString("key", "人人皆满足 座座皆豪华");
XT6Fragment fragment2 = new XT6Fragment();
fragment2.setArguments(bundle);
getSupportFragmentManager().beginTransaction()
.replace(R.id.gridLayout, fragment2).commit();
break;
case 3:
bundle.putString("key", "风驰电掣只需一瞬\n动静姿态皆超越期待");
controlFragment fragment3 = new controlFragment();
fragment3.setArguments(bundle);
getSupportFragmentManager().beginTransaction()
.replace(R.id.gridLayout, fragment3).commit();
break;
case 4:
bundle.putString("key", "座座皆安心 座座皆平稳");
safetyFragment fragment4 = new safetyFragment();
fragment4.setArguments(bundle);
getSupportFragmentManager().beginTransaction()
.replace(R.id.gridLayout, fragment4).commit();
break;
case 5:
bundle.putString("key", "以高效兼容高雅 以简练勾勒繁华");
designFragment fragment5 = new designFragment();
fragment5.setArguments(bundle);
getSupportFragmentManager().beginTransaction()
.replace(R.id.gridLayout, fragment5).commit();
break;
}
}
}));
}
}

Fragment的动态加载

使用碎片(Fragment)的动态加载方法来实现实时切换不同型号车辆的宣传页面

代码部分

主界面

  1. 使用 Bundle 容器使动态创建的碎片能够获取到对应点击对象的数据
  2. 使用 getSupportFragmentManager().beginTransaction().replace(网格布局id, new 碎片).commit(); 的方法动态加载碎片
  3. 使用 setArguments(Bundle变量) 将数据传输到碎片中

以下是相关代码

MainActivity.java
public class MainActivity extends AppCompatActivity {
List<Cadillac> data = new ArrayList<>();//数据源
Bundle bundle = new Bundle();//传输数据的容器

//组件变量
...
private ImageView iv;
private MediaPlayer mediaPlayer;

//在页面销毁时释放MediaPlayer多媒体播放器资源(背景音乐)
@Override
protected void onDestroy() {
super.onDestroy();
mediaPlayer.release();
mediaPlayer = null;
}

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

//初始化多媒体播放器
mediaPlayer = MediaPlayer.create(this, R.raw.coolkids);
mediaPlayer.setLooping(true); //设置循环播放
mediaPlayer.start();

//动态加载Fragment
getSupportFragmentManager().beginTransaction().replace(R.id.gridLayout, new HomeFragment()).commit();

//初始化组件
...
iv = findViewById(R.id.background);

//RecyclerView设置适配器
rv.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
rv.setAdapter(new MyAdapter(this, data, new IsClicked() {
@Override
public void Position(int position) {
if (position < 6) {
iv.setImageResource(data.get(position).getPic());
}
switch (position) {
case 0:
bundle.putString("key", "四驱高性能版 逐 光 而 来");//将要传输到相应的碎片中的数据放入容器
LYRIQFragment fragment = new LYRIQFragment();//创建对应的碎片
fragment.setArguments(bundle);//发送要传输的数据
getSupportFragmentManager().beginTransaction()
.replace(R.id.gridLayout, fragment).commit();//将当前显示的碎片替换成指定的碎片
break;
case 1:
bundle.putString("key", "豪华 不 止 于 所 见");
CT6Fragment fragment1 = new CT6Fragment();
fragment1.setArguments(bundle);
getSupportFragmentManager().beginTransaction()
.replace(R.id.gridLayout, fragment1).commit();
break;
case 2:
bundle.putString("key", "人人皆满足 座座皆豪华");
XT6Fragment fragment2 = new XT6Fragment();
fragment2.setArguments(bundle);
getSupportFragmentManager().beginTransaction()
.replace(R.id.gridLayout, fragment2).commit();
break;
case 3:
bundle.putString("key", "风驰电掣只需一瞬\n动静姿态皆超越期待");
controlFragment fragment3 = new controlFragment();
fragment3.setArguments(bundle);
getSupportFragmentManager().beginTransaction()
.replace(R.id.gridLayout, fragment3).commit();
break;
case 4:
bundle.putString("key", "座座皆安心 座座皆平稳");
safetyFragment fragment4 = new safetyFragment();
fragment4.setArguments(bundle);
getSupportFragmentManager().beginTransaction()
.replace(R.id.gridLayout, fragment4).commit();
break;
case 5:
bundle.putString("key", "以高效兼容高雅 以简练勾勒繁华");
designFragment fragment5 = new designFragment();
fragment5.setArguments(bundle);
getSupportFragmentManager().beginTransaction()
.replace(R.id.gridLayout, fragment5).commit();
break;
}
}
}));
}
}

碎片

  1. 设置视频播放
  2. 使用 getArguments() 方法获取从 MainActivity.java 传输而来的数据
  3. 将获取的数据设置到TextView的内容中
controlFragment.java
package com.example.cadillac;

import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

import com.example.cadillac.databinding.FragmentControlBinding;
import com.example.cadillac.databinding.FragmentHomeBinding;


public class controlFragment extends Fragment {
FragmentControlBinding binding;

private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";

private String mParam1;
private String mParam2;

public controlFragment() {}

public static controlFragment newInstance(String param1, String param2) {
controlFragment fragment = new controlFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentControlBinding.inflate(inflater, container, false);
return binding.getRoot();
}

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);

//播放视频
Uri uri = Uri.parse("android.resource://" + getContext().getPackageName() + "/" + R.raw.v4);//设置视频播放源文件
binding.vv.setVideoURI(uri); // 设置视频资源
binding.vv.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mp.setLooping(true); // 循环播放
}
});
binding.vv.start();//开始播放

//获取从MainActivity传输的数据并赋予TextView
Bundle bundle = getArguments();
String value = bundle.getString("key");
binding.tvMsg.setText(value + "");
}
}

布局与代码关系示意图

1 1

学习心得

凯迪拉克案例让我们综合地、全面地学习了一个商业广告宣传应用的制作方法,其中视频播放、约束布局、RecyclerView、动态加载碎片是学习到的新知识。然而,这些新知识所蕴含的代码并非难以看懂,其逻辑性让我们能够更加容易地理解代码的含义和效果。在学到新知识点的同时巩固了已学过的、常用的知识点,将这样一个比之前学习过的案例更加大型的案例系统地学习一遍能够让我们收获到许多。