今天给以下效果来提供一点思路
常能看到listview中有一个视频文件(或者视频图片,就是那种没有播放的视频预览图),点击之后直接可以在listview上播放出来
一般的效果是:
1、在播放一个视频的时候,不能播放其他的视频
2、在播放一个视频的时候,滑出屏幕,该视频停止播放
3、如果再仔细点,播放视频的暂停和快进按钮也应该设置进来
那这个效果是怎么实现的呢?
布局思路:
1、Listview作为整体
2、item的布局中有视频的相关介绍或发布者介绍,最主要的是一个视频播放控件,然后加一个常见的带有播放标志的小圆图,点击播放就将小圆图隐藏
结构思路:
1、Listview需要适配器、适配器需要实体类、实体类需要数据接口、数据接口需要网络权限或数据库读写权限
2、Listview的适配器中需要item视图xml文件,item视图xml文件中的控件需要id,播放标志和视频控件为叠加关系,所以局部需要帧布局
所需工具搭配思路:
1、有网络请求、图片加载,需要网络请求框架和图片加载框架,数据解析器Gson或FastJson
本例用的OkHttpUtils网络请求框架、Picasso图片加载框架和Gson解析工具包
大致步骤:
1、主activity给个Listview
2、item视图布局布一下
3、视频数据接口写好
4、视频数据的实体类写好
5、Listview的适配器写好,一般是继承于自己写的基础适配器,而自己写的基础适配器(带泛型)是继承自BaseAdapter,这样一来,自己的基础适配器可以被复用多次,注意在getItem的返回值类型中也要写泛型T而不是默认的Object,否则子适配器获取不到实体类中的数据
6、写View层和Presenter层(MVP结构),在activity中find到Listview,设置适配器并加载数据
7、给网络请求权限
具体步骤:
其他都是一些常规步骤,这里只给出listview的适配器的设计步骤
当一页的列表中有许多个item,每个item中都有一个surfaceview和一个表面的imageview
如果滑到某一item并点击,应该有的效果是,表面的imageview被隐藏,同时mediaplayer被获得当前position的视频url并播放,而surfaceview也同时将视频图像播放。
但listview的有一个特点,他的item数据是用adapter加进去的,而且为了listview的加载效率,其中相同布局的item是利用了viewHolder来复用的,那如果直接在viewholder中点击监听,并隐藏图片和播放视频的话,将会在下一次复用时图片继续隐藏掉并播放上一个正在播放的视频,这样整个listview就乱掉了。所以此处的思路不应该是在viewholder的图片点击事件中直接修改当前item的效果,而是给个“开关”,然后刷新适配器,让listview重新布局,布局时遇到刚才点击的那个item,隐藏图片并播放视频,这样就算被复用了布局,也不会出现新的item保持复用前item的情况。而且同时会解决这样一个问题:同一屏幕下的两个item切换播放,由于是适配器刷新,所以不会出现错误。
但这样又会出现两个问题:1、当前item被播放,当滑出去,这个item不会停下来,会继续播放;2、就算该视频看完了,滑出去再滑回来,视频会被自动重新播放。那么就需要判断如果滑出去了,就把mediaplayer关掉并将这个item的surfaceview停止,用mediaplayer的监听setOnCompletionListener置curPosition为-1即可。
列表中的视频播放问题基本解决,还剩下视频自己的问题:第二次按同一个item时,要让这个视频停止,这个事件放在哪里呢?当然也是在视频控件表面的图片控件的点击事件里。也就是这种事件:如果点击的时候mediaplayer正在播放,就停止播放,同时刷新适配器,由于surfaceview和mediaplayer已经绑定,当mediaplayer停止,surfaceview也会在刷新适配器后停止视频展示。
=====================================================
listView的adapter代码如下
package com.qianfeng.listvideoplaymine.adapter;
import android.content.Context;
import android.graphics.Bitmap;
import android.media.MediaPlayer;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.qianfeng.listvideoplaymine.R;
import com.qianfeng.listvideoplaymine.bean.VideoEntity;
import com.squareup.picasso.Picasso;
import java.io.IOException;
/**
* Created by MitnickGuo on 2016-10-28.
*/
public class ListVideoBaseAdapter extends MBaseAdapter<VideoEntity.ItemsBean> implements MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener {
private Context context;
private MediaPlayer mediaPlayer;
private int curPosition=-1;
public ListVideoBaseAdapter(Context context) {
super(context);
this.context=context;
mediaPlayer=new MediaPlayer();
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.setOnCompletionListener(this);
}
@Override
public View getView(int i, View convertView, ViewGroup viewGroup) {
ViewHolder viewHolder;
if (convertView==null) {
convertView=getInflater().inflate(R.layout.item_view,viewGroup,false);
viewHolder=new ViewHolder(convertView);
convertView.setTag(viewHolder);
}
else {
viewHolder= ((ViewHolder) convertView.getTag());
}
viewHolder.titleTx.setText(getItem(i).getUser().getLogin());
viewHolder.contentTv.setText(getItem(i).getContent());
viewHolder.headIcon.setImageResource(R.mipmap.ic_launcher);
Picasso.with(context).load(getItem(i).getPic_url()).config(Bitmap.Config.RGB_565).into(viewHolder.playIcon);
// Log.d("~Pcur", curPosition+"");
// Log.d("~Pi", i+"");
// Log.d("~Ptag", viewHolder.playIcon.getTag()+"");//一个屏幕最多放4个,所以0123是自己的,从4开始复用第0个的item
if(viewHolder.playIcon.getTag()!=null)
{
int pos = (int) viewHolder.playIcon.getTag();
if(pos==curPosition && pos!=i)//pos==curPosition表示:如果复用item出现了,则停止被复用的item的播放,这种其实不太好,如果离复用还有好几个item,就不会在第一时间停止播放
{
if(mediaPlayer.isPlaying())
{
mediaPlayer.stop();
curPosition = -1;
}
}
}
//视频和mediaplayer配置
viewHolder.playIcon.setTag(i);
if(curPosition==i){
viewHolder.playIcon.setVisibility(View.INVISIBLE);
viewHolder.surfaceView.setVisibility(View.VISIBLE);
mediaPlayer.reset();
try {
mediaPlayer.setDisplay(viewHolder.surfaceView.getHolder());
mediaPlayer.setDataSource(getItem(i).getHigh_url());
mediaPlayer.prepareAsync();
} catch (IOException e) {
e.printStackTrace();
}
}
else {
viewHolder.playIcon.setVisibility(View.VISIBLE);
viewHolder.surfaceView.setVisibility(View.INVISIBLE);
}
return convertView;
}
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.start();
}
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
curPosition=-1;
}
class ViewHolder implements View.OnClickListener {
ImageView headIcon, playIcon;
TextView titleTx, contentTv;
SurfaceView surfaceView;
public ViewHolder(View convertView) {
this.headIcon = ((ImageView) convertView.findViewById(R.id.item_view_thumbId));
this.playIcon = ((ImageView) convertView.findViewById(R.id.item_view_picId));
this.titleTx = ((TextView) convertView.findViewById(R.id.item_view_loginId));
this.contentTv = ((TextView) convertView.findViewById(R.id.item_view_contentId));
this.surfaceView = ((SurfaceView) convertView.findViewById(R.id.item_view_surfaceViewId));
playIcon.setOnClickListener(this);
surfaceView.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.item_view_picId:
view.setVisibility(View.INVISIBLE);
curPosition = (int) view.getTag();
break;
case R.id.item_view_surfaceViewId:
if(mediaPlayer.isPlaying()){
mediaPlayer.stop();
curPosition=-1;
}
break;
}
notifyDataSetChanged();
}
}
}
基础Adapter——MBaseAdapter代码 如下
package com.qianfeng.listvideoplaymine.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.widget.BaseAdapter;
import java.util.ArrayList;
import java.util.List;
/**
* Created by MitnickGuo on 2016-10-28.
*/
public abstract class MBaseAdapter<T> extends BaseAdapter {
private List<T> entities;
private LayoutInflater inflater;
public MBaseAdapter(Context context) {
this.entities = new ArrayList<>();
this.inflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return entities.size();
}
@Override
public T getItem(int i) {
return entities.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
public void addAll(List<T> dd){
entities.addAll(dd);
notifyDataSetChanged();
}
public LayoutInflater getInflater() {
return inflater;
}
}
MainActivity代码如下
package com.qianfeng.listvideoplaymine.ui;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.ListView;
import com.google.gson.Gson;
import com.qianfeng.listvideoplaymine.R;
import com.qianfeng.listvideoplaymine.adapter.ListVideoBaseAdapter;
import com.qianfeng.listvideoplaymine.bean.VideoEntity;
import com.qianfeng.listvideoplaymine.uri.MUrlInterface;
import com.zhy.http.okhttp.OkHttpUtils;
import com.zhy.http.okhttp.callback.StringCallback;
import okhttp3.Call;
public class MainActivity extends AppCompatActivity {
private ListView listView;
private ListVideoBaseAdapter listVideoBaseAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onStart() {
super.onStart();
listView = ((ListView) findViewById(R.id.activity_main_listViewId));
listVideoBaseAdapter = new ListVideoBaseAdapter(this);
listView.setAdapter(listVideoBaseAdapter);
loadData();
}
private void loadData() {
OkHttpUtils.get().url(String.format(MUrlInterface.URL_VIDEO,1)).build().execute(new StringCallback() {
@Override
public void onError(Call call, Exception e, int id) {
}
@Override
public void onResponse(String response, int id) {
Gson gson=new Gson();
VideoEntity videoEntity = gson.fromJson(response, VideoEntity.class);
listVideoBaseAdapter.addAll(videoEntity.getItems());
}
});
}
}
使用到的接口,有需要的拿去撸
package com.qianfeng.listvideoplaymine.uri;
/**
* Created by KUODA on 2016-10-28.
*/
public interface MUrlInterface {
// 最新
public final static String URL_LATEST = "http://m2.qiushibaike.com/article/list/latest?page=%d";
// 图片
public final static String URL_PIC= "http://m2.qiushibaike.com/article/list/pic?page=%d";
// 视频
public final static String URL_VIDEO = "http://m2.qiushibaike.com/article/list/video?page=%d";
// 文本
public final static String URL_TEXT = "http://m2.qiushibaike.com/article/list/text?page=%d";
//头像获取(+ id掉后4位 + "/" + id + "/thumb/" + icon图片名.jpg)
//userIcon======http://pic.qiushibaike.com/system/avtnew/1499/14997026/thumb/20140404194843.jpg
public final static String URL_USER_ICO