Android ListView实现QQ空间界面(说说内包含图片、视频、点赞、评论、转发功能),结尾附源码

前段时间刚好需要做一个类似于QQ空间的社区分享功能,说说内容包含文字(话题、内容)、视频、图片,还需包含点赞,评论,位置信息等功能。 就采用LIstview做了一个,先来看下效果,GIF太大,CSDN传不了,请移步Gitee连接:GIF效果

在这里插入图片描述

1. 先来分析一下ListView中每一个条目包含的控件,请看下图

在这里插入图片描述
序号1:头像,ImageView,自定义为圆形即可;
序号2:用户名,TextView;
序号3:发布时间,TextView;
序号4:说说文字部分,TextView;
序号5:说说中视频或图片部分,Videoview;
序号6:点赞信息,TextView动态添加
序号7:位置信息,TextView
序号8/9/10:点赞、评论、转发,均为ImageView
序号11:评论区,TextView动态添加
序号12:评论框,EditText,其右侧图片是通过drawableRight设置的,事件监听会在后面详细说;
上面图中漏了一个,在视频正中央还需要有一个播放按钮,为ImageView,通过切换ImageView中图片实现播放与暂停切换。

2. 确定好有哪些控件后,我们用xml实现布局,文件命名为video_brower_item.xml,代码如下:
<?xml version="1.0" encoding="utf-8"?>

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <LinearLayout
        android:id="@+id/mContainer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:paddingTop="10dp"
        android:background="@android:color/white">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <com.xiaok.winterolympic.custom.CircleImageView
                android:id="@+id/video_avatar"
                android:layout_width="45dp"
                android:layout_height="45dp"
                android:src="@drawable/head_picture" />

            <TextView
                android:id="@+id/video_username"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="xiaok"
                android:textColor="#000000"
                android:layout_marginStart="15dp"
                android:textSize="24sp"
                android:textStyle="bold" />

            <TextView
                android:id="@+id/video_date"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="20dp"
                android:textSize="14sp"
                android:text="刚刚"/>

        </LinearLayout>

        <TextView
            android:id="@+id/video_descripation"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="15dp"
            android:textSize="16sp"
            android:textColor="#000000"
            android:text="#共迎冬奥# 冬奥"/>


        <VideoView
            android:id="@+id/video_view"
            android:layout_width="match_parent"
            android:layout_height="230dp"
            android:layout_marginTop="15dp"/>

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <TextView
                android:id="@+id/video_position"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="北京市朝阳区"
                android:layout_marginTop="12dp"
                android:layout_alignParentStart="true"
                android:layout_marginBottom="10dp"/>

            <ImageView
                android:id="@+id/video_iv_good"
                style="@style/VideoShareImageView"
                android:src="@mipmap/video_share_good"
                android:layout_toStartOf="@+id/video_iv_comment"
                android:layout_marginEnd="20dp"/>
            
            <ImageView
                android:id="@+id/video_iv_comment"
                style="@style/VideoShareImageView"
                android:src="@mipmap/video_share_comment"
                android:layout_toStartOf="@+id/video_iv_share"
                android:layout_marginEnd="20dp"/>

            <ImageView
                android:id="@+id/video_iv_share"
                style="@style/VideoShareImageView"
                android:src="@mipmap/video_share_share"
                android:layout_alignParentEnd="true"
                android:layout_marginEnd="10dp"/>

        </RelativeLayout>

        <EditText
            android:id="@+id/video_et_comment"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:hint="评论"
            android:textSize="14sp"
            android:layout_marginBottom="20dp"
            android:drawableRight="@drawable/video_send_picture"/>


    </LinearLayout>

    <ImageView
        android:id="@+id/video_play"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_record_play"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="192dp"/>

</FrameLayout>

效果如图:
在这里插入图片描述

3. 定义一个类,这里命名为VideoBrower,用于封装ListView中每个条目所用到的数据:
package com.xiaok.winterolympic.model;

import java.io.Serializable;

public class VideoBrower implements Serializable {

    private static final long serialVersionUID = 1L;

    private int avatarId;
    private String username;
    private String date;
    private String videoDescripation;
    private String videoPath;
    private String position;


    public VideoBrower(int avatarId, String username, String date, String videoDescripation, String videoPath, String position) {
        this.avatarId = avatarId;
        this.username = username;
        this.date = date;
        this.videoDescripation = videoDescripation;
        this.videoPath = videoPath;
        this.position = position;
    }

    public int getAvatarId() {
        return avatarId;
    }

    public String getUsername() {
        return username;
    }

    public String getDate() {
        return date;
    }

    public String getVideoDescripation() {
        return videoDescripation;

    }

    public String getVideoPath() {
        return videoPath;
    }

    public String getPosition() {
        return position;
    }

    public void setAvatarId(int avatarId) {
        this.avatarId = avatarId;
    }

    public void setDate(String date) {
        this.date = date;
    }

    public void setUsername(String username) {
        this.username = username;

    }

    public void setVideoDescripation(String videoDescripation) {
        this.videoDescripation = videoDescripation;
    }

    public void setVideoPath(String videoPath) {
        this.videoPath = videoPath;
    }

    public void setPosition(String position) {
        this.position = position;
    }

}

这里解释下,头像我是通过封装R文件中对应的资源ID实现的,所以格式为int

4. 定义这个ListViewAdapter,命名为VideoBrowerAdapter

这里有几个点需要单独说明,

  1. VideoView中视频加载完还未播放之前,其背景一般需要显示视频的预览,通常取视频的第一帧图片作为视频预览显示在VideoView中,获取视频第一帧通常通过MediaMetadataRetriever,代码如下:
		MediaMetadataRetriever media = new MediaMetadataRetriever();
        Uri videoUri = Uri.parse(aData.get(position).getVideoPath()); //getVideoPath()获取的是视频的存储路径
        media.setDataSource(mContext,videoUri);
        Bitmap bitmap = media.getFrameAtTime(); 
  1. 在设计评论框时,我使用的是EditText加上其内置的drawableRight属性,但EditText原生并不提供关于drawable属性的监听,所以这里我通过回调onTouch,监测用户触摸的位置,进而实现drawable的交互,代码如下:
		//评论框右边发表图标
        holder.video_et_comment.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // et.getCompoundDrawables()得到一个长度为4的数组,分别表示左右上下四张图片
                Drawable drawable = holder.video_et_comment.getCompoundDrawables()[2];
                //如果右边没有图片,不再处理
                if (drawable == null)
                    return false;
                //如果不是按下事件,不再处理
                if (event.getAction() != MotionEvent.ACTION_UP)
                    return false;
                if (event.getX() > holder.video_et_comment.getWidth()
                        - holder.video_et_comment.getPaddingRight()
                        - drawable.getIntrinsicWidth()){
                    //发表
                    String commentStr = holder.video_et_comment.getText().toString().trim();
                    if (TextUtils.isEmpty(commentStr)){
                        ToastUtils.showSingleToast("评论内容不能为空");
                    }else {
                        addView(holder, commentStr);
                        holder.video_et_comment.setText(""); //发表完评论后编辑框清空
                        ToastUtils.showSingleToast("发表成功!");
                    }

                }
                return false;
            }
        });

完整代码如下:

public class VideoBrowerAdapter extends BaseAdapter {

    private LinkedList<VideoBrower> aData;
    private Context mContext;
    private boolean isGood = false;
    private boolean isVideoPlaying = false;
    private int commentIndex = 4;

    private TextView thumpUpView;

    public VideoBrowerAdapter(LinkedList<VideoBrower> aData, Context mContext){
        this.aData = aData;
        this.mContext = mContext;
    }
    @Override
    public int getCount(){
        return aData.size();
    }
    @Override
    public  Object getItem(int position){
        return null;
    }
    @Override
    public long getItemId(int position){
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent){
        ViewHolder holder;
        if (convertView==null){
            convertView = LayoutInflater.from(mContext).inflate(R.layout.video_brower_item,parent,false);
            holder = new ViewHolder();
            holder.videoAvatar = convertView.findViewById(R.id.video_avatar);
            holder.username = convertView.findViewById(R.id.video_username);
            holder.videoDate = convertView.findViewById(R.id.video_date);
            holder.videoDescripation = convertView.findViewById(R.id.video_descripation);
            holder.video_view = convertView.findViewById(R.id.video_view);
            holder.videoPosition = convertView.findViewById(R.id.video_position);
            holder.videoPlay = convertView.findViewById(R.id.video_play);
            holder.video_iv_good = convertView.findViewById(R.id.video_iv_good);
            holder.video_iv_comment = convertView.findViewById(R.id.video_iv_comment);
            holder.video_iv_share = convertView.findViewById(R.id.video_iv_share);
            holder.video_et_comment = convertView.findViewById(R.id.video_et_comment);

            holder.mContainer = convertView.findViewById(R.id.mContainer); //拿到布局,用于动态添加View
            convertView.setTag(holder);
        }else {
            holder = (ViewHolder)convertView.getTag();
        }
        MediaMetadataRetriever media = new MediaMetadataRetriever();
        Uri videoUri = Uri.parse(aData.get(position).getVideoPath());
        media.setDataSource(mContext,videoUri);
        Bitmap bitmap = media.getFrameAtTime();

        holder.videoAvatar.setImageResource(aData.get(position).getAvatarId());
        holder.username.setText(aData.get(position).getUsername());
        holder.videoDate.setText(aData.get(position).getDate());
        holder.videoDescripation.setText(aData.get(position).getVideoDescripation());
        holder.video_view.setBackground(new BitmapDrawable(bitmap));
        holder.video_view.setVideoURI(videoUri);
        holder.videoPosition.setText(aData.get(position).getPosition());

        /*
        *响应事件
         */
        //播放视频按钮
        holder.videoPlay.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (holder.video_view != null){
                    if (!isVideoPlaying){
                        holder.videoPlay.setImageResource(R.mipmap.ic_record_stop);
                        holder.video_view.start();
                        holder.video_view.setBackground(null);
                        holder.video_view.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                            @Override
                            public void onCompletion(MediaPlayer mp) {
                                holder.videoPlay.setImageResource(R.mipmap.ic_record_play);
                            }
                        });
                    }else {
                        holder.video_view.stopPlayback();
                        holder.videoPlay.setImageResource(R.mipmap.ic_record_play);
                    }

                }
            }
        });

        //点赞
        holder.video_iv_good.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!isGood){
                    holder.video_iv_good.setImageResource(R.mipmap.video_share_good_blue);
                    addThumpUpView(holder);
                    Toast.makeText(mContext, "点赞成功!", Toast.LENGTH_SHORT).show();
                    isGood = true;
                }else {
                    holder.video_iv_good.setImageResource(R.mipmap.video_share_good);
                    removeThumpUpView(holder);
                    isGood = false;
                }

            }
        });

        //评论图片按钮
        holder.video_iv_comment.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //焦点移动到评论区
                holder.video_et_comment.requestFocus();
            }
        });

        //评论框右边发表图标
        holder.video_et_comment.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // et.getCompoundDrawables()得到一个长度为4的数组,分别表示左右上下四张图片
                Drawable drawable = holder.video_et_comment.getCompoundDrawables()[2];
                //如果右边没有图片,不再处理
                if (drawable == null)
                    return false;
                //如果不是按下事件,不再处理
                if (event.getAction() != MotionEvent.ACTION_UP)
                    return false;
                if (event.getX() > holder.video_et_comment.getWidth()
                        - holder.video_et_comment.getPaddingRight()
                        - drawable.getIntrinsicWidth()){
                    //发表
                    String commentStr = holder.video_et_comment.getText().toString().trim();
                    if (TextUtils.isEmpty(commentStr)){
                        ToastUtils.showSingleToast("评论内容不能为空");
                    }else {
                        addView(holder, commentStr);
                        holder.video_et_comment.setText(""); //发表完评论后编辑框清空
                        ToastUtils.showSingleToast("发表成功!");
                    }

                }
                return false;
            }
        });
		
		//分享
        holder.video_iv_share.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //分享说说,具体功能就不写了
            }
        });


        return convertView;
    }
    static class ViewHolder{
        CircleImageView videoAvatar;
        TextView username;
        TextView videoDate;
        TextView videoDescripation;
        VideoView video_view;
        TextView videoPosition;
        ImageView videoPlay;
        ImageView video_iv_good;
        ImageView video_iv_comment;
        ImageView video_iv_share;
        EditText video_et_comment;
        LinearLayout mContainer;
    }
	
	/**
	* 添加评论
	* @param:holder:ListView容器,用于
	* @param:commentStr:评论内容
	*/
    private void addView(ViewHolder holder, String commentStr){
        TextView view = new TextView(mContext);
        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 60);
        lp.setMargins(0,15,0,15);
        view.setTextSize(14);
        view.setTextColor(mContext.getColor(R.color.record_comment_text));
        view.setText("xiaok:"+commentStr);
        int index = holder.mContainer.getChildCount();
        holder.mContainer.addView(view,index-1,lp);
    }

    private void addThumpUpView(ViewHolder holder){
        thumpUpView = new TextView(mContext);
        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 60);
        lp.setMargins(0,15,0,15);
        thumpUpView.setTextSize(14);
        thumpUpView.setTextColor(mContext.getColor(R.color.record_comment_text));
        thumpUpView.setText("xiaok 觉得很赞");
        holder.mContainer.addView(thumpUpView,3,lp);
    }

    private void removeThumpUpView(ViewHolder holder){
        holder.mContainer.removeViewAt(3);
    }
}
5. 创建Activity对应的布局文件,命名为activity_video_brower.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".view.video.VideoBrowerActivity">

    <ListView
        android:id="@+id/video_listview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

    </ListView>

</LinearLayout>
6. 创建Activity文件
public class VideoBrowerActivity extends AppCompatActivity {

    private List<VideoBrower> aDate;
    private int[]videoAvatars;
    private String[]usernames;
    private String[]videoDates;
    private String[]videoDescripation;
    private String[]videoPaths;
    private String[]videoPosition;
    private String videoDescripation_01;

    private ListView lv_video;
    private ImageButton ib_back;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_video_brower);

        setCustomActionBar();  //加载自定义ActionBar

        lv_video = findViewById(R.id.video_listview);

        videoDescripation_01 = getIntent().getStringExtra("videoDescripation");

        videoAvatars = new int[]{R.drawable.head_picture,R.mipmap.video_avatar_02,R.mipmap.video_avatar_03,R.mipmap.video_avatar_04};
        usernames = new String[]{"xiaok","小张","王小明","李晓华"};
        videoDates = new String[]{"刚刚","09月17日10:12","08月22日18:15","08月22日17:42"};
        videoDescripation = new String[]{videoDescripation_01,"#雪容融和冰墩墩# 冬奥会和冬残奥会吉祥物来袭!","#北京冬奥# 北京赢了!2022冬奥,不见不散!!!","#冬奥来了# 冬奥宣传视频,浓浓的中国风,超赞!"};
        videoPaths = new String[]{FileUtils.VIDEO_01,FileUtils.VIDEO_02,FileUtils.VIDEO_03,FileUtils.VIDEO_04};
        videoPosition = new String[]{"北京市朝阳区","郑州市中原区","郑州市中原区","郑州市中原区"};

        aDate = new LinkedList<>();
        for (int i=0;i<videoAvatars.length;i++){
            aDate.add(new VideoBrower(videoAvatars[i],usernames[i],videoDates[i],
                    videoDescripation[i],videoPaths[i],videoPosition[i]));
        }

        lv_video.setAdapter(new VideoBrowerAdapter((LinkedList<VideoBrower>)aDate,VideoBrowerActivity.this));
        lv_video.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

            }
        });


        //左上角返回
        ib_back.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(VideoBrowerActivity.this,MainPageActivity.class));
            }
        });
    }

    private void setCustomActionBar() {
        ActionBar.LayoutParams lp =new ActionBar.LayoutParams(ActionBar.LayoutParams.MATCH_PARENT, ActionBar.LayoutParams.MATCH_PARENT, Gravity.CENTER);
        View mActionBarView = LayoutInflater.from(this).inflate(R.layout.record_share_actionbar, null);
        ib_back = mActionBarView.findViewById(R.id.record_ib_back);
        ActionBar actionBar = getSupportActionBar();
        actionBar.setCustomView(mActionBarView, lp);
        actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
        actionBar.setDisplayShowCustomEnabled(true);
        actionBar.setDisplayHomeAsUpEnabled(false);
        actionBar.setHomeButtonEnabled(false);
        actionBar.setDisplayShowTitleEnabled(false);
    }

项目源码地址:地址

欢迎大佬们给star!

  • 12
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
实现ListView点赞和收藏功能,你需要在ListView的每个item中添加两个按钮,一个是点赞按钮,一个是收藏按钮。 1. 首先,在ListView的item布局文件中添加点赞和收藏按钮: ```xml <LinearLayout ... android:orientation="horizontal"> <!-- 左侧图标 --> <ImageView ... android:id="@+id/icon" /> <!-- 中间布局 --> <LinearLayout ... android:orientation="vertical"> <TextView ... android:id="@+id/title" /> <TextView ... android:id="@+id/content" /> </LinearLayout> <!-- 右侧操作按钮 --> <LinearLayout ... android:orientation="horizontal"> <!-- 点赞按钮 --> <Button ... android:id="@+id/like_btn" android:text="赞" /> <!-- 收藏按钮 --> <Button ... android:id="@+id/favorite_btn" android:text="收藏" /> </LinearLayout> </LinearLayout> ``` 2. 在ListView的Adapter中,为点赞和收藏按钮添加点击事件,并更新数据源: ```java public class MyAdapter extends BaseAdapter { private List<Item> mDataList; public MyAdapter(List<Item> dataList) { mDataList = dataList; } @Override public int getCount() { return mDataList.size(); } @Override public Item getItem(int position) { return mDataList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(final int position, View convertView, ViewGroup parent) { ViewHolder viewHolder; if (convertView == null) { convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false); viewHolder = new ViewHolder(convertView); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } final Item item = mDataList.get(position); viewHolder.icon.setImageResource(item.getIcon()); viewHolder.title.setText(item.getTitle()); viewHolder.content.setText(item.getContent()); viewHolder.likeBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { item.setLiked(!item.isLiked()); notifyDataSetChanged(); } }); viewHolder.favoriteBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { item.setFavorite(!item.isFavorite()); notifyDataSetChanged(); } }); return convertView; } public static class ViewHolder { ImageView icon; TextView title; TextView content; Button likeBtn; Button favoriteBtn; public ViewHolder(View itemView) { icon = itemView.findViewById(R.id.icon); title = itemView.findViewById(R.id.title); content = itemView.findViewById(R.id.content); likeBtn = itemView.findViewById(R.id.like_btn); favoriteBtn = itemView.findViewById(R.id.favorite_btn); } } } ``` 3. 在Item类中添加isLiked和isFavorite属性,以及相关的getter和setter方法: ```java public class Item { private int icon; private String title; private String content; private boolean isLiked; private boolean isFavorite; public Item(int icon, String title, String content) { this.icon = icon; this.title = title; this.content = content; this.isLiked = false; this.isFavorite = false; } public int getIcon() { return icon; } public void setIcon(int icon) { this.icon = icon; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public boolean isLiked() { return isLiked; } public void setLiked(boolean liked) { isLiked = liked; } public boolean isFavorite() { return isFavorite; } public void setFavorite(boolean favorite) { isFavorite = favorite; } } ``` 这样,就可以实现ListView点赞和收藏功能了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值