智能电视TV开发---直播视频客户端结构设计和实现
在智能电视TV开发---客户端和服务器通信里面我们实现了客户端和服务端的简单通信,接下来我们做一个简单的客户端界面,来实现手机端来操控智能电视的TV端。
一、存储视频的结构设计
我们在做客户端的时候,通常是需要存储视频的相关信息,结构如下:
package com.jwzhangjie.smarttv_client.model; import android.os.Parcel; import android.os.Parcelable; public class LiveModel implements Parcelable{ /** * 数据库位置 */ private int db_id; /** * 直播频道的id */ private int channel_id; /** * 直播频道的名称 */ private String channel_name; /** * 直播频道的url */ private String icon_url; /** * 直播频道的省份 */ private String province; /** * 直播频道清晰度 */ private String mode; /** * 直播频道的链接 */ private String url; /** * 直播频道的second_url */ private String second_url; /** * 直播频道所属的类型 */ private String types; public LiveModel(){ } private LiveModel(Parcel parcel){ readFromParcel(parcel); } @Override public int describeContents() { return 0; } public void readFromParcel(Parcel parcel){ db_id = parcel.readInt(); channel_id = parcel.readInt(); channel_name = parcel.readString(); icon_url = parcel.readString(); province = parcel.readString(); mode = parcel.readString(); url = parcel.readString(); second_url = parcel.readString(); types = parcel.readString(); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(db_id); dest.writeInt(channel_id); dest.writeString(channel_name); dest.writeString(icon_url); dest.writeString(province); dest.writeString(mode); dest.writeString(url); dest.writeString(second_url); dest.writeString(types); } public static Creator<LiveModel> CREATOR = new Creator<LiveModel>() { @Override public LiveModel createFromParcel(Parcel source) { return new LiveModel(source); } @Override public LiveModel[] newArray(int size) { return new LiveModel[size]; } }; public int getDb_id() { return db_id; } public void setDb_id(int db_id) { this.db_id = db_id; } public int getChannel_id() { return channel_id; } public void setChannel_id(int channel_id) { this.channel_id = channel_id; } public String getChannel_name() { return channel_name; } public void setChannel_name(String channel_name) { this.channel_name = channel_name; } public String getIcon_url() { return icon_url; } public void setIcon_url(String icon_url) { this.icon_url = icon_url; } public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } public String getMode() { return mode; } public void setMode(String mode) { this.mode = mode; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getSecond_url() { return second_url; } public void setSecond_url(String second_url) { this.second_url = second_url; } public String getTypes() { return types; } public void setTypes(String types) { this.types = types; } }
二、数据库设计
接下来我们设计数据库表格,代码如下:
package com.jwzhangjie.smarttv_client.db; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper; import android.os.Environment; public class DBHelper extends SQLiteOpenHelper{ private static final String NAME = "tv_jie.sqlite"; public static final String DB_PATH = Environment.getExternalStorageDirectory() + "/"; private static final int DB_VERSION = 1; public static final String LIVE_VIDEO="CREATE TABLE live_video " + "('db_id' INTEGER PRIMARY KEY AUTOINCREMENT, 'channel_id' INTEGER," + " 'channel_name' VARCHAR, 'icon_url' VARCHAR, 'province' VARCHAR, 'mode' VARCHAR, " + "'url' VARCHAR, 'second_url' VARCHAR, 'types' VARCHAR)"; public DBHelper(Context context) { super(context, DB_PATH + NAME, null, DB_VERSION); } public DBHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(LIVE_VIDEO); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF live_video"); } }
三、调用数据库,首先数据的插入,或直播,获取直播的个数
package com.jwzhangjie.smarttv_client.utils; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import com.jwzhangjie.smarttv_client.db.DBHelper; import com.jwzhangjie.smarttv_client.model.LiveVideoModel; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; public class DBUtils implements Serializable{ private static final long serialVersionUID = 1L; private static DBHelper mDBHelper; public DBUtils(Context paramContext) { mDBHelper = new DBHelper(paramContext); } public void close() { if (mDBHelper != null) { mDBHelper.close(); } } public void insertLiveVideo(LiveVideoModel liveModel){ SQLiteDatabase db = null; try { db = mDBHelper.getWritableDatabase(); ContentValues contentValues = new ContentValues(); contentValues.put("channel_id", liveModel.getChannel_id()); contentValues.put("channel_name", liveModel.getChannel_name()); contentValues.put("icon_url", liveModel.getIcon_url()); contentValues.put("province", liveModel.getProvince()); contentValues.put("mode", liveModel.getMode()); contentValues.put("url", liveModel.getUrl()); contentValues.put("second_url", liveModel.getSecond_url()); contentValues.put("types", liveModel.getTypes()); db.insertOrThrow("live_video", null, contentValues); } catch (Exception e) { e.printStackTrace(); } } /** * 获取所有的直播视频的个数 * @return */ public int getMovieCount() { SQLiteDatabase db = mDBHelper.getReadableDatabase(); Cursor cursor = db.rawQuery("SELECT count(*) FROM live_video", null); cursor.moveToNext(); int coutn = cursor.getInt(0); cursor.close(); db.close(); return coutn; } public List<LiveVideoModel> getLiveVideoModels(){ List<LiveVideoModel> listData = new ArrayList<LiveVideoModel>(); try { SQLiteDatabase db = mDBHelper.getReadableDatabase(); Cursor cursor = db.rawQuery("SELECT * FROM live_video", null); while (cursor.moveToNext()) { LiveVideoModel mVideoModel = new LiveVideoModel(); mVideoModel.setDb_id(cursor.getInt(0)); mVideoModel.setChannel_id(cursor.getInt(1)); mVideoModel.setChannel_name(cursor.getString(2)); mVideoModel.setIcon_url(cursor.getString(3)); mVideoModel.setProvince(cursor.getString(4)); mVideoModel.setMode(cursor.getString(5)); mVideoModel.setUrl(cursor.getString(6)); mVideoModel.setSecond_url(cursor.getString(7)); mVideoModel.setTypes(cursor.getString(8)); listData.add(mVideoModel); } } catch (Exception e) { e.printStackTrace(); } return listData; } }具体的数据自己去填充,这里我就不提供了,各大视频论坛都分享
四、客户端视频数据的显示
这里已经假设数据库已经有数据了,界面很简单就是一个listView来显示,自定义一个adapter
4.1ListView中Item的设计
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:andro android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > <ImageView android: android:layout_width="wrap_content" android:layout_height="wrap_content" android:scaleType="center" android:layout_centerVertical="true" android:src="@drawable/widget_progress_medium_rotation_image" android:contentDescription="@string/app_name" /> <ImageView android: android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/arrow_right" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_marginRight="10dip" android:contentDescription="@string/app_name" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_toRightOf="@id/item_icon" android:layout_toLeftOf="@id/arrow_right" android:orientation="vertical" android:gravity="center_vertical" android:layout_centerVertical="true" android:layout_marginLeft="10dip" > <TextView android: android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="title" /> <TextView android: android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="subtitle" /> </LinearLayout> </RelativeLayout>
4.2网络图片加载方式
这里涉及的直播频道的图标都是网络图片,所以要实现加载网络图片的功能,这里我使用的是网络一个开源的库com.nostra13.universalimageloader,在网上一搜就能看见,所以要使用它我们要配置一些内容。 首先编写一个类继承Application,然后再AndroidManifest.xml里面设置Application的name属性,代码如下:
package com.jwzhangjie.smarttv_client.utils; import com.nostra13.universalimageloader.cache.disc.naming.Md5FileNameGenerator; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; import com.nostra13.universalimageloader.core.assist.QueueProcessingType; import android.app.Application; import android.content.Context; public class SmartTV_App extends Application { @Override public void onCreate() { super.onCreate(); initImageLoader(getApplicationContext()); } public static void initImageLoader(Context context) { ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder( context).threadPriority(Thread.NORM_PRIORITY - 2) .denyCacheImageMultipleSizesInMemory() .discCacheFileNameGenerator(new Md5FileNameGenerator()) .tasksProcessingOrder(QueueProcessingType.LIFO) .writeDebugLogs() // Remove for release app .build(); ImageLoader.getInstance().init(config); } }
4.3ListView的adapter设计
package com.jwzhangjie.smarttv_client.adapter; import java.util.List; import com.jwzhangjie.smarttv_client.R; import com.jwzhangjie.smarttv_client.model.LiveVideoModel; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.ImageLoader; import android.content.Context; import android.graphics.Bitmap; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; public class LiveVideoAdapter extends BaseAdapter{ private LayoutInflater mInflater; private List<LiveVideoModel> listDatas; private ImageLoader imageLoader = ImageLoader.getInstance(); private DisplayImageOptions options; public LiveVideoAdapter(Context context){ mInflater = LayoutInflater.from(context); options = new DisplayImageOptions.Builder() .showImageOnLoading(R.drawable.img_loading_bg) .showImageForEmptyUri(R.drawable.img_loading_empty) .showImageOnFail(R.drawable.img_loading_error) .cacheInMemory(true) .cacheOnDisc(true) .bitmapConfig(Bitmap.Config.RGB_565) .build(); } public void updateListDatas(List<LiveVideoModel> listDatas){ this.listDatas = listDatas; notifyDataSetChanged(); } @Override public int getCount() { return listDatas == null ?0:listDatas.size(); } @Override public Object getItem(int position) { return listDatas.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder viewHolder; if (convertView == null) { viewHolder = new ViewHolder(); convertView = mInflater.inflate(R.layout.item_live, null); viewHolder.item_icon = (ImageView)convertView.findViewById(R.id.item_icon); viewHolder.item_arrow = (ImageView)convertView.findViewById(R.id.arrow_right); viewHolder.item_title = (TextView)convertView.findViewById(R.id.item_title); viewHolder.item_subtitle = (TextView)convertView.findViewById(R.id.item_subtitle); convertView.setTag(viewHolder); }else { viewHolder = (ViewHolder)convertView.getTag(); } LiveVideoModel videoModel = listDatas.get(position); viewHolder.item_title.setText(videoModel.getChannel_name()); viewHolder.item_subtitle.setText("省份:"+videoModel.getProvince()); imageLoader.displayImage(videoModel.getIcon_url(), viewHolder.item_icon, options); return convertView; } class ViewHolder{ private ImageView item_icon; private ImageView item_arrow; private TextView item_title; private TextView item_subtitle; } }
4.4主界面布局设计
<RelativeLayout xmlns:andro xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".Smart_TV_Client" > <RelativeLayout android: android:layout_width="match_parent" android:layout_height="60dip" android:background="#d0d0d0" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="直播界面测试" android:layout_centerInParent="true" android:textSize="16sp" android:textStyle="bold" /> </RelativeLayout> <ListView android: android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/title" android:cacheColorHint="#00000000" android:divider="@drawable/cbox_uc_divider" android:fadeScrollbars="false" android:fastScrollEnabled="false" android:focusable="true" android:focusableInTouchMode="true" android:scrollbars="none" android:scrollingCache="false" ></ListView> </RelativeLayout>
4.5 主界面代码编写
这里就是这章最后的一步,调用数据库数据,然后传值给adapter,显示在listview上面
package com.jwzhangjie.smarttv_client; import java.util.List; import com.jwzhangjie.smarttv_client.adapter.LiveVideoAdapter; import com.jwzhangjie.smarttv_client.model.LiveVideoModel; import com.jwzhangjie.smarttv_client.utils.DBUtils; import android.os.AsyncTask; import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.view.Window; import android.widget.ListView; public class Smart_TV_Client extends Activity { private ListView listView; private LiveVideoAdapter adapter; private DBUtils dbUtils; private List<LiveVideoModel> listDatas; @Override protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); super.onCreate(savedInstanceState); setContentView(R.layout.activity_smart__tv__client); dbUtils = new DBUtils(this); initView(); loadData(); } public void initView(){ adapter = new LiveVideoAdapter(this); listView = (ListView)findViewById(R.id.listview); listView.setAdapter(adapter); } public void loadData(){ new loadData().execute(); } private class loadData extends AsyncTask<Void, Void, Void>{ @Override protected Void doInBackground(Void... params) { listDatas = dbUtils.getLiveVideoModels(); return null; } @Override protected void onPostExecute(Void result) { adapter.updateListDatas(listDatas); super.onPostExecute(result); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.smart__tv__client, menu); return true; } }