Android项目实战系列—基于博学谷(七)课程模块(中)
2020-05-06 本文已影响0人
微笑涛声
image
在
2、导入界面图片
在
在
在
在
image
文件名输入
image
由于默认的编码会导致软件运行以后
image
(1)、找到
(2)、找到
(3)、找到
(4)、找到
(5)、找到
image
image
由于这个模块内容较多,分为上、中、下 三篇博客分别来讲述,请耐心阅读。
课程模块分为四个部分
课程模块(中)主要讲述课程详情部分
一、课程详情界面
1、创建课程详情界面
在com.boxuegu.activity
包中创建一个Java类命名为VideoListActivity
。在res/layout
文件夹下创建一个布局文件,命名为activity_video_list
。
2、导入界面图片 default_video_list_icon.png
、vodeo_list_intro_icon.png
3、界面代码——activity_video_list.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:orientation="vertical">
<TextView
android:layout_width="fill_parent"
android:layout_height="200dp"
android:background="@drawable/default_video_list_icon"/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="50dp"
android:gravity="center"
android:orientation="horizontal">
<RelativeLayout
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1"
android:background="@drawable/video_list_intro_icon">
<TextView
android:layout_width="fill_parent"
android:layout_height="46dp"
android:id="@+id/tv_intro"
android:layout_centerVertical="true"
android:background="#30B4FF"
android:gravity="center"
android:text="简 介"
android:textColor="#FFFFFF"
android:textSize="20sp"/>
</RelativeLayout>
<View
android:layout_width="1dp"
android:layout_height="48dp"
android:background="#C3C3C3"/>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1"
android:background="@drawable/video_list_intro_icon">
<TextView
android:layout_width="fill_parent"
android:layout_height="46dp"
android:id="@+id/tv_video"
android:layout_centerVertical="true"
android:background="#FFFFFF"
android:gravity="center"
android:text="视 频"
android:textColor="#000000"
android:textSize="20sp"/>
</RelativeLayout>
</LinearLayout>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ListView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/lv_video_list"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:divider="#E4E4E4"
android:dividerHeight="1dp"
android:scrollbars="none"
android:visibility="gone"/>
<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/sv_chapter_intro">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal">
<TextView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/tv_chapter_intro"
android:lineSpacingMultiplier="1.5"
android:padding="10dp"
android:text="wordpress简介"
android:textColor="@android:color/black"
android:textSize="14sp"/>
</LinearLayout>
</ScrollView>
</RelativeLayout>
</LinearLayout>
二、课程详情界面Item
1、创建课程详情界面item
在res/layout
文件夹下创建一个布局文件,命名为video_list_item
。导入所需图片 course_bar_icon.png
。
2、界面代码——video_list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:gravity="center_vertical"
android:paddingBottom="15dp"
android:paddingTop="15dp">
<ImageView
android:id="@+id/iv_left_icon"
android:layout_width="25dp"
android:layout_height="25dp"
android:src="@drawable/course_bar_icon" />
<TextView
android:id="@+id/tv_video_title"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginLeft="10dp"
android:gravity="center_vertical"
android:textColor="#333333"
android:textSize="14sp"/>
</LinearLayout>
三、创建VideoBean
在com.boxuegu.bean
包中创建一个VideoBean
类,用来创建视频的所有属性。
package com.boxuegu.bean;
public class VideoBean {
public int chapterId; //章节ID
public int videoId; //视频ID
public String title; //章节标题
public String secondTitle; //视频标题
public String videoPath; //视频播放地址
}
四、课程界面Adapter
在com.boxuegu.adapter
包中创建一个VideoListAdapter
类。代码如下:
package com.boxuegu.adapter;
import android.content.Context;
import android.graphics.Color;
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 java.util.List;
import com.boxuegu.R;
import com.boxuegu.bean.VideoBean;
//列表
public class VideoListAdapter extends BaseAdapter{
private Context mContext;
private List<VideoBean> vbl; //视频列表数据
private int selectedPosition = -1; //点击时选中的位置
private OnSelectListener onSelectListener;
public VideoListAdapter(Context context,OnSelectListener onSelectListener){
this.mContext = context;
this.onSelectListener = onSelectListener;
}
public void setSelectedPosition(int position){
selectedPosition = position;
}
//设置数据更新界面
public void setData(List<VideoBean> vbl){
this.vbl = vbl;
notifyDataSetChanged();
}
//获取Item的总数
@Override
public int getCount() {
return vbl == null ? 0 : vbl.size();
}
//根据position得到item对应的对象
@Override
public VideoBean getItem(int position) {
return vbl == null ? null : vbl.get(position);
}
//根据position得到item对应的ID
@Override
public long getItemId(int position) {
return position;
}
//得到对应position的item视图
@Override
public View getView(final int position, View view, ViewGroup viewGroup) {
final ViewHolder vh;
if (view == null){
vh = new ViewHolder();
view = LayoutInflater.from(mContext).inflate(
R.layout.video_list_item,null
);
vh.tv_title = (TextView) view.findViewById(R.id.tv_video_title);
vh.iv_icon = (ImageView) view.findViewById(R.id.iv_left_icon);
view.setTag(vh);
}else {
vh = (ViewHolder) view.getTag();
}
final VideoBean bean = getItem(position);
vh.iv_icon.setImageResource(R.drawable.course_bar_icon);
vh.tv_title.setTextColor(Color.parseColor("#333333"));
if (bean!=null){
vh.tv_title.setText(bean.secondTitle);
//设置选中效果
if (selectedPosition == position){
vh.iv_icon.setImageResource(R.drawable.course_intro_icon);
vh.tv_title.setTextColor(Color.parseColor("#009958"));
}else {
vh.iv_icon.setImageResource(R.drawable.course_bar_icon);
vh.tv_title.setTextColor(Color.parseColor("#333333"));
}
}
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (bean == null)
return;
//播放视频
onSelectListener.onSelect(position,vh.iv_icon);
}
});
return view;
}
class ViewHolder{
public TextView tv_title;
public ImageView iv_icon;
}
//创建接口,传递position和ImageView
public interface OnSelectListener{
void onSelect(int position,ImageView iv);
}
}
五、视频列表数据的存放
1、Eclipse新建json文件,并改变文件的编码。
在assets
文件夹中右键
,选择new
,点击file
。
image
文件名输入data.json
image
由于默认的编码会导致软件运行以后中文出现乱码
。在data.json
文件里面,使用Alt+Enter
快捷键弹出修改编码的对话框,编码方式选择UTF-8
即可。
image
2、data.json代码如下:
[
{
"chapterId": 1,
"videoId": "1",
"title": "第1章 博客(网站)简介",
"secondTitle": "什么是博客",
"videoPath": "video11.mp4"
},
{
"chapterId": 1,
"videoId": "2",
"title": "第1章 博客(网站)简介",
"secondTitle": "网站与博客的关系",
"videoPath": "beyond.mp4"
},
{
"chapterId": 2,
"videoId": "1",
"title": "第2章 Linux系统简介",
"secondTitle": "认识Linux",
"videoPath": ""
},
{
"chapterId": 2,
"videoId": "2",
"title": "第2章 Linux系统简介",
"secondTitle": "CentOS 7简单操作",
"videoPath": ""
},
{
"chapterId": 3,
"videoId": "1",
"title": "第3章 WordPress简介",
"secondTitle": "动态博客系统",
"videoPath": ""
},
{
"chapterId": 3,
"videoId": "2",
"title": "第3章 WordPress简介",
"secondTitle": "WordPress系统",
"videoPath": ""
},
{
"chapterId": 4,
"videoId": "1",
"title": "第4章 安装MySQL数据库",
"secondTitle": "MySQL数据库简介",
"videoPath": ""
},
{
"chapterId": 4,
"videoId": "2",
"title": "第4章 安装MySQL数据库",
"secondTitle": "安装MySQL数据库",
"videoPath": ""
},
{
"chapterId": 5,
"videoId": "1",
"title": "第5章 数据存储服务器",
"secondTitle": "常见的服务器",
"videoPath": ""
},
{
"chapterId": 5,
"videoId": "2",
"title": "第5章 数据存储服务器",
"secondTitle": "Nginx的安装",
"videoPath": ""
},
{
"chapterId": 6,
"videoId": "1",
"title": "第6章 安装PHP环境",
"secondTitle": "PHP语法简介",
"videoPath": ""
},
{
"chapterId": 6,
"videoId": "2",
"title": "第6章 安装PHP环境",
"secondTitle": "安装PHP",
"videoPath": ""
},
{
"chapterId": 7,
"videoId": "1",
"title": "第7章 云服务器的购买",
"secondTitle": "购买云服务器",
"videoPath": ""
},
{
"chapterId": 7,
"videoId": "2",
"title": "第7章 云服务器的购买",
"secondTitle": "云服务器的简单配置",
"videoPath": ""
},
{
"chapterId": 8,
"videoId": "1",
"title": "第8章 域名购买及备案",
"secondTitle": "购买域名",
"videoPath": ""
},
{
"chapterId": 8,
"videoId": "2",
"title": "第8章 域名购买及备案",
"secondTitle": "域名备案",
"videoPath": ""
},
{
"chapterId": 9,
"videoId": "1",
"title": "第9章 安装WordPress",
"secondTitle": "安装前准备",
"videoPath": ""
},
{
"chapterId": 9,
"videoId": "2",
"title": "第9章 安装WordPress",
"secondTitle": "安装WordPress",
"videoPath": ""
},
{
"chapterId": 10,
"videoId": "1",
"title": "第10章 WordPress简单设置",
"secondTitle": "更换博客主题",
"videoPath": ""
},
{
"chapterId": 10,
"videoId": "2",
"title": "第10章 WordPress简单设置",
"secondTitle": "博客基本布局设置",
"videoPath": ""
}
]
六、课程详情界面逻辑代码
1、界面代码——VideoListActivity.java
package com.boxuegu.activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.graphics.Color;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import com.boxuegu.R;
import com.boxuegu.adapter.VideoListAdapter;
import com.boxuegu.bean.VideoBean;
import com.boxuegu.utils.AnalysisUtils;
import com.boxuegu.utils.DBUtils;
public class VideoListActivity extends AppCompatActivity implements View.OnClickListener {
private String intro;
private int chapterId;
private DBUtils db;
private ArrayList<VideoBean> videoList;
private TextView tv_intro;
private TextView tv_video;
private ListView lv_video_list;
private TextView tv_chapter_intro;
private ScrollView sv_chapter_intro;
private VideoListAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_list);
//设置界面为视频
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
//从课程界面传过来的章节ID
chapterId = getIntent().getIntExtra("id", 0);
//从课程界面传过来的章节简介
intro = getIntent().getStringExtra("intro");
db = DBUtils.getInstance(VideoListActivity.this);
initData();
initView();
}
//初始化界面UI控件
private void initView() {
tv_intro = (TextView) findViewById(R.id.tv_intro);
tv_video = (TextView) findViewById(R.id.tv_video);
lv_video_list = (ListView) findViewById(R.id.lv_video_list);
tv_chapter_intro = (TextView) findViewById(R.id.tv_chapter_intro);
sv_chapter_intro = (ScrollView)findViewById(R.id.sv_chapter_intro);
adapter = new VideoListAdapter(this, new VideoListAdapter.OnSelectListener() {
@Override
public void onSelect(int position, ImageView iv) {
adapter.setSelectedPosition(position); //设置适配器的选中项
VideoBean bean = videoList.get(position);
String videoPath = bean.videoPath;
//notifyDataSetChanged刷新UI
adapter.notifyDataSetChanged();
if (TextUtils.isEmpty(videoPath)){
Toast.makeText(VideoListActivity.this,"本地没有此视频,暂时无法播放",Toast.LENGTH_SHORT).show();
return;
}else {
//判断用户是否登录,若登录则把视频添加到记录中
if (readLoginStatus()){
String userName = AnalysisUtils.readLoginUserName(VideoListActivity.this);
db.saveVideoPlayList(videoList.get(position),userName);
}
//跳转到视频播放界面
}
}
});
lv_video_list.setAdapter(adapter);
tv_intro.setOnClickListener(this);
tv_video.setOnClickListener(this);
adapter.setData(videoList);
tv_chapter_intro.setText(intro);
tv_intro.setBackgroundColor(Color.parseColor("#30B4FF"));
tv_video.setBackgroundColor(Color.parseColor("#FFFFFF"));
tv_intro.setTextColor(Color.parseColor("#FFFFFF"));
tv_video.setTextColor(Color.parseColor("#000000"));
}
/**
* 设置视频列表本地数据
*/
private void initData() {
//多个视频,用jsonArray
JSONArray jsonArray;
InputStream is = null;
try {
is = getResources().getAssets().open("data.json");
jsonArray = new JSONArray(read(is));
videoList = new ArrayList<VideoBean>();
for (int i = 0;i < jsonArray.length();i++){
VideoBean bean = new VideoBean();
JSONObject jsonObj = jsonArray.getJSONObject(i);
if(jsonObj.getInt("chapterId") == chapterId){
bean.chapterId = jsonObj.getInt("chapterId");
bean.videoId = Integer.parseInt(jsonObj.getString("videoId"));
bean.title = jsonObj.getString("title");
bean.secondTitle = jsonObj.getString("secondTitle");
bean.videoPath = jsonObj.getString("videoPath");
videoList.add(bean);
}
bean = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
/*
*读取数据流,参数in就是数据流,把输入流转换成字符串
*/
private String read(InputStream in) {
BufferedReader reader = null;
StringBuilder sb = null;
String line = null;
try {
sb = new StringBuilder(); //实例化一个StringBulider对象
//InoutStreamReader把in字节流转换成字符流
reader = new BufferedReader(new InputStreamReader(in));
while ((line = reader.readLine()) != null) {
sb.append(line);
sb.append("\n");
}
} catch (IOException e) {
e.printStackTrace();
return "";
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return sb.toString();
}
/**
* 从SharedPreferences读取登录状态
*/
private boolean readLoginStatus() {
SharedPreferences sp = getSharedPreferences("loginInfo", Context.MODE_PRIVATE);
boolean isLogin = sp.getBoolean("isLogin",false);
return isLogin;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (data!=null){
//
int position = data.getIntExtra("position",0);
adapter.setSelectedPosition(position);
//
lv_video_list.setVisibility(View.VISIBLE);
sv_chapter_intro.setVisibility(View.GONE);
tv_intro.setBackgroundColor(Color.parseColor("#FFFFFF"));
tv_video.setBackgroundColor(Color.parseColor("#30B4FF"));
tv_intro.setTextColor(Color.parseColor("#000000"));
tv_video.setTextColor(Color.parseColor("#FFFFFF"));
}
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.tv_intro: //简介
lv_video_list.setVisibility(View.GONE);
sv_chapter_intro.setVisibility(View.VISIBLE);
tv_intro.setBackgroundColor(Color.parseColor("#30B4FF"));
tv_video.setBackgroundColor(Color.parseColor("#FFFFFF"));
tv_intro.setTextColor(Color.parseColor("#FFFFFF"));
tv_video.setTextColor(Color.parseColor("#000000"));
break;
case R.id.tv_video: //视频
lv_video_list.setVisibility(View.VISIBLE);
sv_chapter_intro.setVisibility(View.GONE);
tv_intro.setBackgroundColor(Color.parseColor("#FFFFFF"));
tv_intro.setTextColor(Color.parseColor("#000000"));
tv_video.setBackgroundColor(Color.parseColor("#30B4FF"));
tv_video.setTextColor(Color.parseColor("#FFFFFF"));
break;
default:
break;
}
}
}
2、修改代码
(1)、找到SQLiteHelper.java
文件,在 public static final String U_USERINFO = "userinfo";
下方添加如下代码:
public static final String U_VIDEO_PLAY_LIST = "videoplaylist"; //视频播放列表
(2)、找到SQLiteHelper.java
文件,在onCreate()
方法里面添加如下代码:
//创建视频播放记录
db.execSQL("CREATE TABLE IF NOT EXISTS " + U_VIDEO_PLAY_LIST + "( " +
"_id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "userName VARCHAR,"//用户名
+ "chapterId INT," //章节id
+ "videoId INT,"//小节id
+ "videoPath VARCHAR,"
+ "title VARCHAR," //章节名字
+ "secondTitle VARCHAR" // 视频名字
+ ")");
(3)、找到SQLiteHelper.java
文件,在onUpgrade()
方法里面的onCreate(db);
语句前添加如下代码:
db.execSQL("DROP TABLE IF NOT EXISTS " + U_VIDEO_PLAY_LIST);
(4)、找到DBUtils.java
文件,添加如下代码:
//保存视频播放记录
public void saveVideoPlayList(VideoBean bean, String userName) {
//判断如果里面已经有此记录则需要先删除再重新存放
if (hasVideoPlay(bean.chapterId, bean.videoId, userName)) {
boolean isDelete = delVideoPlay(bean.chapterId, bean.videoId, userName);
if (!isDelete) {
//没有删除成功
return;
}
}
ContentValues cv = new ContentValues();
cv.put("userName", userName);
cv.put("chapterId", bean.chapterId);
cv.put("videoId", bean.videoId);
cv.put("videoPath", bean.videoPath);
cv.put("title", bean.title);
cv.put("secondTitle", bean.secondTitle);
db.insert(SQLiteHelper.U_VIDEO_PLAY_LIST, null, cv);
}
/**
* 删除已经存在的是视屏记录
* @param chapterId
* @param videoId
* @param userName
* @return
*/
public boolean delVideoPlay(int chapterId, int videoId, String userName) {
boolean delSuccess = false;
int row = db.delete(SQLiteHelper.U_VIDEO_PLAY_LIST, " chapterId=? AND videoId=? AND userName=?",
new String[]{chapterId + "", videoId + "", userName});
if (row > 0) {
delSuccess = true;
}
return delSuccess;
}
/**
* 判断视频记录是否存在
* @param chapterId
* @param videoId
* @param userName
* @return
*/
public boolean hasVideoPlay(int chapterId, int videoId, String userName) {
boolean hasVideo = false;
String sql = "SELECT * FROM " + SQLiteHelper.U_VIDEO_PLAY_LIST + " WHERE chapterId=? AND videoId=? AND userName =?";
Cursor cursor = db.rawQuery(sql, new String[]{ chapterId + "", videoId + "", userName});
if (cursor.moveToFirst()) {
hasVideo = true;
}
cursor.close();
return hasVideo;
}
(5)、找到CourseAdapter.java
文件,在两处
注释//跳转到课程详情界面
分别添加以下代码:
Intent intent = new Intent(mContext, VideoListActivity.class);
intent.putExtra("id",bean.id);
intent.putExtra("intro",bean.intro);
mContext.startActivity(intent);
七、运行效果
imageAndroid项目实战系列—基于博学谷 开源地址
image
image