本项目是用eclipse软件编写,经过我的亲自实践,其真实有效,希望能给您有所帮助 项目版本:android5.1.1 AVD建议:android4.4.2及以上 若有不足之处或不对的地方,欢迎大佬们指点 |
课程详情界面用于展示每章节课程简介和视频列表
点击课程界面列表中的条目,会跳转到课程详情界面。课程详情界面是显示每章节的课程简介和视频列表。课程简介部分展示该章节的简介,视频列表 部分展示该章节所包含的课程视频。当用户点击视频列表中的某个条目时,会播放相应的视频
将课程详情界面所需图片default_video_list_icon.png
、video_list_intro_icon.png
导入 到drawable
文件夹中
注意,第二张图片是透明的矩形图片:540 x 48
activity_video_list.xml
res/layout
文件夹中新建activity_video_list.xml
文件,代码如下:
<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:id="@+id/tv_intro"
android:layout_width="fill_parent"
android:layout_height="46dp"
android:layout_centerVertical="true"
android:background="#30B4FF"
android:gravity="center"
android:text="@string/tv_intro"
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:id="@+id/tv_video"
android:layout_width="fill_parent"
android:layout_height="46dp"
android:layout_centerVertical="true"
android:background="#FFFFFF"
android:gravity="center"
android:text="@string/tv_video"
android:textColor="#000000"
android:textSize="20sp" />
RelativeLayout>
LinearLayout>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<ListView
android:id="@+id/lv_video_list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:divider="#E4E4E4"
android:dividerHeight="1dp"
android:scrollbars="none"
android:visibility="gone" />
<ScrollView
android:id="@+id/sv_chapter_intro"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<TextView
android:id="@+id/tv_chapter_intro"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:lineSpacingMultiplier="1.5"
android:padding="10dp"
android:text="@string/tv_chapter_intro"
android:textColor="@android:color/black"
android:textSize="14sp" />
LinearLayout>
ScrollView>
RelativeLayout>
LinearLayout>
在values文件夹下的string.xml
文件里面输入文本信息:
<string name="tv_intro">简 介string>
<string name="tv_video">视 频string>
<string name="tv_chapter_intro">安卓简介string>
Item
由于课程详情界面用到了ListView
控件,因此需要为该控件创建一个Item
界面
Item
界面所需图片course_bar_icon.png
导入到drawable
文件夹中res/layout
文件夹中,创建一个布局文件video_list_item.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="wrap_content"
android:background="@android:color/white"
android:gravity="center_vertical"
android:orientation="horizontal"
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:text="[第一节]Android简介"
android:textColor="#333333"
android:textSize="14sp" />
</LinearLayout>
VideoBean
由于每个章节的视频详细信息都会有章节id 、视频id 、章节标题、视频标题、视频播放地址等属性,因此需要创建一个VideoBean
类来存放这些属性。
在china.ynyx.heyunhui.bean
包中创建一个VideoBean
类:
package china.ynyx.heyunhui.bean;
public class VideoBean {
public int chapterId;// 章节Id
public int videoId;// 视频Id
public String title;// 章节标题
public String secondTitle;// 视频标题
public String videoPath;// 视频播放地址
}
Adapter
由于课程详情界面的视频列表用的是ListView
控件,因此需要创建一个数据适配器 VideoListAdapter
对ListView
进行数据适配
在 china.ynyx.heyunhui.adapter
包中,创建一个VideoListAdapter
类继承 BaseAdapter
类
具体代码如下:VideoListAdapter.java
package china.ynyx.heyunhui.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 china.ynyx.heyunhui.R;
import china.ynyx.heyunhui.bean.VideoBean;
import java.util.List;
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视图,
* position是当前Item的位置,
* convertView参数就是滚出屏幕的Item的View
*/
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolder vh;
if (convertView == null) {
vh = new ViewHolder();
convertView = LayoutInflater.from(mContext).inflate(
R.layout.video_list_item, null);
vh.tv_title = (TextView) convertView
.findViewById(R.id.tv_video_title);
vh.iv_icon = (ImageView) convertView
.findViewById(R.id.iv_left_icon);
convertView.setTag(vh);
} else {
vh = (ViewHolder) convertView.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"));
}
}
convertView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (bean == null)
return;
// 播放视频
onSelectListener.onSelect(position, vh.iv_icon);
}
});
return convertView;
}
class ViewHolder {
public TextView tv_title;
public ImageView iv_icon;
}
/**
* 创建OnSelectListener接口把位置position和控件ImageView传递到Activity界面
*/
public interface OnSelectListener {
void onSelect(int position, ImageView iv);
}
}
视频列表部分需要展示相关的视频信息,因此需要在assect
文件夹中创建一个data
,json
文件保存视频相关信息。data.json
文件中包含有章节id
、视频id
、章节名称、视频名称和视频地址。由于博学谷项目使用的是本地数据,因此需要在data.json
文件中指定视频所在位置。
选中assets
文件夹,然后右击并选择File
选项,创建一个data.json
文件,把视频列表的数据以JSON
的格式存放在data.json
文件中。
注意:如果运行的时候有乱码,那就在电脑桌面上新建一个txt
文件,添加内容后保存为UTF-8
编码格式,然后将文件名及后缀改为data.json
具体代码如下:data.json
[
{
"chapterId": 1,
"videoId": "1",
"title": "第1章 Android 基础入门",
"secondTitle": "Android系统简介",
"videoPath": "video11.mp4"
},
{
"chapterId": 1,
"videoId": "2",
"title": "第1章 Android 基础入门",
"secondTitle": "笔记软件",
"videoPath": ""
},
{
"chapterId": 2,
"videoId": "1",
"title": "第2章 Android UI开发",
"secondTitle": "帧布局",
"videoPath": ""
},
{
"chapterId": 2,
"videoId": "2",
"title": "第2章 Android UI开发",
"secondTitle": "表格布局",
"videoPath": ""
},
{
"chapterId": 3,
"videoId": "1",
"title": "第3章 Activity",
"secondTitle": "Activity的启动模式",
"videoPath": ""
},
{
"chapterId": 3,
"videoId": "2",
"title": "第3章 Activity",
"secondTitle": "Activity中的数据传递",
"videoPath": ""
},
{
"chapterId": 4,
"videoId": "1",
"title": "第4章 数据存储",
"secondTitle": "文件存储",
"videoPath": ""
},
{
"chapterId": 4,
"videoId": "2",
"title": "第4章 数据存储",
"secondTitle": "XML序列化与SharedPreferences",
"videoPath": ""
},
{
"chapterId": 5,
"videoId": "1",
"title": "第5章 SQLite 数据库",
"secondTitle": "SQLite数据库的使用",
"videoPath": ""
},
{
"chapterId": 5,
"videoId": "2",
"title": "第5章 SQLite 数据库",
"secondTitle": "ListView控件的使用",
"videoPath": ""
},
{
"chapterId": 6,
"videoId": "1",
"title": "第6章 广播接收者",
"secondTitle": "内容提供者简介",
"videoPath": ""
},
{
"chapterId": 6,
"videoId": "2",
"title": "第6章 广播接收者",
"secondTitle": "内容提供者的访问",
"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章 网络编程",
"secondTitle": "访问网络",
"videoPath": ""
},
{
"chapterId": 9,
"videoId": "2",
"title": "第9章 网络编程",
"secondTitle": "数据的提交方式",
"videoPath": ""
},
{
"chapterId": 10,
"videoId": "1",
"title": "第10章 高级编程",
"secondTitle": "图形图像处理与动画",
"videoPath": ""
},
{
"chapterId": 10,
"videoId": "2",
"title": "第10章 高级编程",
"secondTitle": "多媒体、与Fragment",
"videoPath": ""
}
]
U_VIDEO_PLAY_LIST
表由于播放视频后会有一个视频播放记录,因此需要在数据库中创建一个视频播放的表
找到.sqlite
包中的SQLiteHelper.java
类中,找到public static final String U_USERINFO = "userinfo";
语句,在该语句下方添加如下代码:
public static final String U_VIDEO_PLAY_LIST = "videoplaylist";//视频播放列表
找到SQLiteHelper
类中的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"// 视频名称
+ ")");
用户播放过的视频会有一个视频记录,因此需要创建一个saveVideoPlayList()
方法将播放过的视频信息保存到数据库中。同一用户在第二 次点击同一个视频时,会判断此视频是否已经存在于数据库,若存在,则删除此视频并 重新保存最新的视频信息。
在.utils
包中的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);
}
/**
* 判断视频记录是否存在
*/
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;
}
/**
* 删除已经存在的视频记录
*/
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;
}
课程详情界面主要展示课程简介和视频列表,当点击“课程简介”按钮时,展示课程简介的布局;当点击“视频列表”按钮时,展示视频列表布局。在视频列表中,视频列表数据是根据课程界面传递过来的章节id获取的,当用户是登录状态并点击了视频列表时,会把点击过的视频信息保存到数据库中,然后在播放记录界面显示出来
在.activity
包中新建VideoListActivity.java
文件
package china.ynyx.heyunhui.activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
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 china.ynyx.heyunhui.R;
import china.ynyx.heyunhui.adapter.VideoListAdapter;
import china.ynyx.heyunhui.bean.VideoBean;
import china.ynyx.heyunhui.utils.AnalysisUtils;
import china.ynyx.heyunhui.utils.DBUtils;
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;
public class VideoListActivity extends AppCompatActivity implements View.OnClickListener{
private TextView tv_intro, tv_video, tv_chapter_intro;
private ListView lv_video_list;
private ScrollView sv_chapter_intro;
private VideoListAdapter adapter;
private List<VideoBean> videoList;
private int chapterId;
private String intro;
private DBUtils db;
@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();
init();
}
/**
* 初始化界面UI控件
*/
private void init() {
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;
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"));
}
/**
* 控件的点击事件
*/
@Override
public void onClick(View v) {
switch (v.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_video.setBackgroundColor(Color.parseColor("#30B4FF"));
tv_intro.setTextColor(Color.parseColor("#000000"));
tv_video.setTextColor(Color.parseColor("#FFFFFF"));
break;
default:
break;
}
}
/**
* 设置视频列表本地数据
*/
private void initData() {
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();//实例化一个StringBuilder对象
//用InputStreamReader把in这个字节流转换成字符流BufferedReader
reader = new BufferedReader(new InputStreamReader(in));
while ((line = reader.readLine())!=null){//从reader中读取一行的内容判断是否为空
sb.append(line);
sb.append("\n");
}
} catch (IOException e) {
e.printStackTrace();
return "";
} finally {
try {
if (in != null)
in.close();
if (reader != null)
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;
}
}
由于课程详情界面是通过课程界面数据适配器跳转的,因此需要找CourseAdapter.java
文件中的getView()
方法,在该方法中的两个注释“//跳转到课程详情界面”
下方分别添加如下代码,并导入对应的包:
Intent intent = new Intent(mContext,VideoListActivity.class);
intent.putExtra("id", bean.id);
intent.putExtra("intro", bean.intro);
mContext.startActivity(intent);
AndroidManifest.xml
文件在AndroidManifest.xml
文件中添加如下代码:
<activity android:name="china.ynyx.heyunhui.activity.VideoListActivity"></activity>
参考资料:《android项目实战——博学谷》(黑马程序员著)