Android_listview_video安卓列表视频直接播放

本文介绍了如何在Android的ListView中实现列表项内的视频直接播放,包括点击播放、滑出屏幕停止播放、暂停和快进控制等功能。通过使用SurfaceView、MediaPlayer和ImageView,结合ListView的适配器和ViewHolder,解决了列表项复用导致的播放混乱问题。同时,文章提到了适配器刷新、滑出屏幕停止播放和再次点击停止播放的处理策略。
摘要由CSDN通过智能技术生成


今天给以下效果来提供一点思路

常能看到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
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值