Kotlin高仿微信-第25篇-朋友圈-显示列表

 Kotlin高仿微信-项目实践58篇详细讲解了各个功能点,包括:注册、登录、主页、单聊(文本、表情、语音、图片、小视频、视频通话、语音通话、红包、转账)、群聊、个人信息、朋友圈、支付服务、扫一扫、搜索好友、添加好友、开通VIP等众多功能。

Kotlin高仿微信-项目实践58篇,点击查看详情

效果图:

实现代码:

<?xml version="1.0" encoding="utf-8"?>
<layout>

    <androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/moment_root"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/wc_base_bg">


        <com.aspsine.swipetoloadlayout.SwipeToLoadLayout
            android:id="@+id/swipeToLoadLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <com.wn.wechatclientdemo.view.CircleRefreshHeaderView
                android:id="@+id/swipe_refresh_header"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/swipe_target"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:scrollbars="vertical" />

            <com.wn.wechatclientdemo.view.LoaderMoreView
                android:id="@+id/swipe_load_more_footer"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:padding="20dp" />
        </com.aspsine.swipetoloadlayout.SwipeToLoadLayout>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

/**
 * Author : wangning
 * Email : maoning20080809@163.com
 * Date : 2022/5/23 16:00
 * Description : 朋友圈
 */
class DiscoverMomentsFragment : BaseDataBindingFragment<WcDiscoverMomentsBinding>(), OnRefreshListener, OnLoadMoreListener {

    override fun getLayoutRes() = R.layout.wc_discover_moments

    private val momentsViewModel : MomentsViewModel by viewModels()
    private val userViewModel : UserViewModel by viewModels()
    private var navController: NavController? = null
    private val REQ_PICTURE_CODE = 101
    private val REQ_VIDEO_CODE = 102

    private var momentsAdapter : DiscoverMomentsAdapter? = null

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        if(!EventBus.getDefault().isRegistered(this)){
            EventBus.getDefault().register(this)
        }

        swipeToLoadLayout.setOnRefreshListener(this)
        swipeToLoadLayout.setOnLoadMoreListener(this)

        var account = DataStoreUtils.getAccount()
        userViewModel.getUserLocal(account)
        navController = findNavController()

        navController?.currentBackStackEntry?.savedStateHandle?.getLiveData<Boolean>(CommonUtils.Moments.PUBLISH_SUCCESS)?.observe(viewLifecycleOwner){
            TagUtils.d("朋友圈发布返回")
            if(it){
                momentsViewModel.getMomentsListLocalEvent()
            }
        }

        TagUtils.d("朋友圈重新主页 ")
        var momentsList = mutableListOf<MomentsBean>()
        //第一行默认写入个人信息
        momentsList.add(MomentsBean())
        momentsAdapter = DiscoverMomentsAdapter(momentsList, PopupWindowClick())

        userViewModel.userBeanLocal.observe(viewLifecycleOwner){
            TagUtils.d("4朋友圈返回用户$it")
            if(it == null) return@observe
            momentsAdapter?.setUserBean(it)
        }

        //使用EventBus刷新数据
        momentsViewModel.getMomentsListLocalEvent()
        var linearLayoutManager = LinearLayoutManager(requireActivity())
        linearLayoutManager.orientation = LinearLayoutManager.VERTICAL
        swipe_target.layoutManager = linearLayoutManager
        swipe_target.adapter = momentsAdapter

        //先加载本地,然后刷新服务器的数据
        momentsViewModel.getMomentsListServerEvent()
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    fun onMessageCallback(momentsEventBean: MomentsEventBean) {

        if(momentsEventBean == null){
            return
        }

        if(momentsEventBean.list == null || momentsEventBean.list.size < 1){
            return
        }

        var list: MutableList<MomentsBean> = momentsEventBean.list
        if(momentsEventBean.type == MomentsEventBean.TYPE_LOCAL_LIST){
            var momentsList = mutableListOf<MomentsBean>()
            //第一行默认写入个人信息
            momentsList.add(MomentsBean())
            TagUtils.d("DiscoverFragment onMessageCallback 个数${list.size} ")
            momentsList.addAll(list)
            momentsAdapter?.refresh(momentsList)
        } else {
            CoroutineScope(Dispatchers.IO).launch {
                //从服务器获取到数据,插入本地数据库
                MomentsRepository.insertMomentListLocal(list)
                //查询最新的刷新页面
                momentsViewModel.getMomentsListLocalEvent()
            }
        }
    }

    override fun onDestroyView() {
        super.onDestroyView()
        TagUtils.d("DiscoverFragment onDestroyView")
        EventBus.getDefault().unregister(this)
    }

    inner class PopupWindowClick : WcOnItemClickInterface {
        override fun onItemClick(obj: Any) {
            showPopupWindow()
        }
    }

    private fun showPopupWindow(){
        var popupView = layoutInflater.inflate(R.layout.wc_moments_pop_view , moment_root, false)
        var popupWindow = PopupWindow(popupView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, true)
        popupWindow.showAtLocation(moment_root, Gravity.BOTTOM, 0, 0)

        var window = requireActivity().window
        //popupWindow在弹窗的时候背景半透明
        val params = window.attributes
        params.alpha = 0.5f
        window.attributes = params
        popupWindow.setOnDismissListener {
            params.alpha = 1.0f
            window.attributes = params
        }

        //拍照小视频
        popupView.findViewById<LinearLayout>(R.id.moments_pop_video).setOnClickListener {
            popupWindow.dismiss()
            var bundle = bundleOf(CameraFragment.TYPE_ENTER to CameraFragment.TYPE_MOMENT)
            navController?.navigate(R.id.action_svideo_camera, bundle)
        }

        //选择相册
        popupView.findViewById<AppCompatTextView>(R.id.moments_pop_album).setOnClickListener {
            popupWindow.dismiss()
            openAblum()
        }

        //选择小视频
        popupView.findViewById<AppCompatTextView>(R.id.moments_pop_svideo).setOnClickListener {
            popupWindow.dismiss()
            openVideo()
        }

        //取消
        popupView.findViewById<AppCompatTextView>(R.id.moments_pop_cancel).setOnClickListener {
            popupWindow.dismiss()
        }

    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        if (requestCode == REQ_PICTURE_CODE && data != null) {
            val images = data.getStringArrayListExtra(ImageSelector.SELECT_RESULT)
            val isCameraImage = data.getBooleanExtra(ImageSelector.IS_CAMERA_IMAGE, false)
            TagUtils.d("ImageSelector", "是否是拍照图片:" + isCameraImage)
            images?.map {
                TagUtils.d("返回图片:${it}")
            }
            if(images != null && images.size > 0) {
                var bundle = bundleOf(CommonUtils.Moments.TYPE_NAME to CommonUtils.Moments.TYPE_PICTURE, CommonUtils.Moments.TYPE_IMAGE_PATH to images)
                navController?.navigate(R.id.action_moments_publish, bundle)
            } else {
                TagUtils.d("请选择图片")
            }
        } else if (requestCode == REQ_VIDEO_CODE && resultCode == RESULT_OK && null != data) {
            var selectedVideo = data.getData()!!
            var filePathColumn = arrayOf(MediaStore.Video.Media.DATA)
            var cursor : Cursor? = requireActivity().contentResolver.query(selectedVideo, filePathColumn, null, null, null);
            cursor?.moveToFirst();

            var columnIndex : Int? = cursor?.getColumnIndex(filePathColumn[0]);
            var videoPath = cursor?.getString(columnIndex!!);
            cursor?.close();
            TagUtils.d("选择小视频路径:${videoPath}")
            var bundle = bundleOf(CommonUtils.Moments.TYPE_NAME to CommonUtils.Moments.TYPE_VIDEO, CommonUtils.Moments.TYPE_VIDEO_PATH to videoPath)
            navController?.navigate(R.id.action_moments_publish, bundle)
        }
    }

    fun openAblum(){
        // 打开相册
        ImageSelector.builder()
            .useCamera(false) // 设置是否使用拍照
            .setSingle(false) //设置是否单选
            .canPreview(true) //是否点击放大图片查看,,默认为true
            .start(this,REQ_PICTURE_CODE)

    }

    //选择小视频
    fun openVideo(){
        val i = Intent(Intent.ACTION_PICK, MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
        startActivityForResult(i, REQ_VIDEO_CODE)
    }

    override fun onRefresh() {
        CoroutineScope(Dispatchers.Main).launch {
            delay(200)
            swipeToLoadLayout.isRefreshing = false
        }
    }

    override fun onLoadMore() {
        CoroutineScope(Dispatchers.Main).launch {
            delay(200)
            swipeToLoadLayout.isLoadingMore = false
        }
    }


}

/**
 * Author : wangning
 * Email : maoning20080809@163.com
 * Date : 2022/5/26 17:55
 * Description : 朋友圈适配器
 */
class DiscoverMomentsAdapter(var list : MutableList<MomentsBean>, var userClick : WcOnItemClickInterface) : RecyclerView.Adapter<DiscoverMomentsAdapter.DiscoverMomentsHolder>(){

    private var userBean : UserBean? = null

    override fun getItemCount() = list.size

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DiscoverMomentsHolder {
        var view = LayoutInflater.from(parent.context).inflate(R.layout.wc_discover_moments_item, parent, false)
        return DiscoverMomentsHolder(view)
    }

    override fun onBindViewHolder(holder: DiscoverMomentsHolder, position: Int) {

        //TagUtils.d("朋友圈position = ${position} , ${userBean}" )
        if(position == 0){
            //朋友圈个人头像信息
            holder.personalView.visibility = View.VISIBLE
            holder.momentsView.visibility = View.GONE
            userBean?.let {
                holder.personalView.process(it, userClick)
            }
        } else {
            holder.personalView.visibility = View.GONE
            holder.momentsView.visibility = View.VISIBLE
            holder.momentsView.process(list.get(position))
        }
    }

    fun setUserBean(userBean: UserBean) {
        this.userBean = userBean
        notifyDataSetChanged()
    }

    //下拉刷新
    fun refresh(list : MutableList<MomentsBean>){
        this.list = list
        notifyDataSetChanged()
    }

    //加载更多
    fun add(list : MutableList<MomentsBean>){
        this.list.addAll(list)
        notifyDataSetChanged()
    }

    class DiscoverMomentsHolder(itemView : View) : RecyclerView.ViewHolder(itemView) {

        var momentsView  = itemView.findViewById<DiscoverMomentsView>(R.id.discover_moments_item_view)
        var personalView  = itemView.findViewById<DiscoverMomentsPersonalView>(R.id.discover_moments_item_personal_view)

    }
}
/**
 * Author : wangning
 * Email : maoning20080809@163.com
 * Date : 2022/5/26 15:29
 * Description : 朋友圈自定义view
 */
class DiscoverMomentsView : RelativeLayout{

    constructor(context: Context) : this(context, null)

    constructor(context: Context, attributeSet: AttributeSet?) : this(context, attributeSet, 0)

    constructor(context: Context, attributeSet: AttributeSet?, defStyleAttr : Int) : super(context, attributeSet, defStyleAttr)

    init {
        LayoutInflater.from(context).inflate(R.layout.wc_discover_moments_view, this)
    }


    fun process(momentsBean: MomentsBean){
        momentsBean?.let {
            discover_moments_content.text = it.content
            discover_moments_time.text = CommonUtils.Date.getCurrentDate(it.addTime)
            BaseUtils.showAvatar(momentsBean.account, discover_moments_icon, discover_moments_name)

            if(it.type == CommonUtils.Moments.TYPE_PICTURE){
                //图片
                discover_moments_recyclerview.visibility = View.VISIBLE
                discover_moments_video.visibility = View.GONE
                discover_moments_video_icon.visibility = View.GONE

                //TagUtils.d("朋友圈图片:" + it.images)
                var imagesList = it.images.split(CommonUtils.Moments.FILE_PATH_MARK)
                var adapter = MomentsPublishAdapter(2, imagesList, object : WcOnItemClickInterface{
                    override fun onItemClick(obj: Any) {
                        TagUtils.d("点击:${obj}")
                        var imagePath = obj as String
                        var bundle = bundleOf(CommonUtils.Moments.TYPE_IMAGE_PATH to imagePath,
                            CommonUtils.Moments.TYPE_NAME to CommonUtils.Moments.TYPE_PICTURE,
                            CommonUtils.Chat.IS_HIDE_CONFIRM to true)
                        Navigation.findNavController(discover_moments_recyclerview).navigate(R.id.action_svideo_play, bundle)
                    }
                })
                var linearLayoutManager = GridLayoutManager(context , 3)
                //linearLayoutManager.orientation = LinearLayoutManager.HORIZONTAL
                discover_moments_recyclerview.layoutManager = linearLayoutManager
                discover_moments_recyclerview.adapter = adapter


            } else if(it.type == CommonUtils.Moments.TYPE_VIDEO){
                //小视频
                discover_moments_recyclerview.visibility = View.GONE
                if(File(momentsBean.videoLocal).exists()){
                    TagUtils.d("本地小视频存在:${momentsBean.videoLocal}")
                    //小视频文件存在, 直接显示播放
                    discover_moments_video_icon.visibility = View.GONE
                    discover_moments_video.visibility = View.VISIBLE
                    playVideo(momentsBean.videoLocal)

                    discover_moments_video.setOnClickListener {
                        showVideo(discover_moments_video, momentsBean.videoLocal)
                    }

                } else {
                    //小视频文件不存在, 默认先显示缩略图, 然后下载
                    discover_moments_video_icon.visibility = View.VISIBLE
                    discover_moments_video.visibility = View.GONE
                    GlideUtils.load(discover_moments_video_icon, CommonUtils.Base.getReallyImage(momentsBean.images))

                    var videoUrl = CommonUtils.Moments.getReallyImageUrl(momentsBean.video)
                    TagUtils.d("小视频地址:${momentsBean.video} , ${videoUrl}")
                    TagUtils.d("小视频缩略图:${momentsBean.images} , ${CommonUtils.Base.getReallyImage(momentsBean.images)}")
                    var  videoFile = FileUtils.getBaseFile(momentsBean.video)
                    VideoDownloadManager.download(videoUrl, videoFile, object : VideoDownloadInter{
                        override fun onDone(filePath: String) {
                            TagUtils.d("小视频下载完成:${filePath}")
                            CoroutineScope(Dispatchers.Main).launch {
                                discover_moments_video_icon.visibility = View.GONE
                                discover_moments_video.visibility = View.VISIBLE
                                momentsBean.videoLocal = filePath
                                playVideo(filePath)

                                CoroutineScope(Dispatchers.IO).launch {
                                    //更新到本地数据库
                                    MomentsRepository.updateMomentLocal(momentsBean)
                                }

                                discover_moments_video.setOnClickListener {
                                    showVideo(discover_moments_video, momentsBean.videoLocal)
                                }

                            }
                        }

                        override fun onError() {
                            CoroutineScope(Dispatchers.Main).launch {
                                TagUtils.d("小视频下载失败:${momentsBean.video}")
                            }
                        }

                        override fun onProgress(process: Int) {

                        }
                    })
                }

            } else {
                //文字
                discover_moments_recyclerview.visibility = View.GONE
                discover_moments_video.visibility = View.GONE
            }
        }
    }

    //点击查看小视频
    private fun showVideo(view: View, videoPath : String){
        var bundle = bundleOf(CommonUtils.Moments.TYPE_VIDEO_PATH to videoPath,
            CommonUtils.Moments.TYPE_NAME to CommonUtils.Moments.TYPE_VIDEO,
            CommonUtils.Chat.IS_HIDE_CONFIRM to true)
        Navigation.findNavController(view).navigate(R.id.action_svideo_play, bundle)
    }

    /**
     * 播放小视频
     * @param filePath String
     */
    private fun playVideo(filePath : String){
        discover_moments_video.setVideoPath(filePath)
        discover_moments_video.requestFocus()
        discover_moments_video.start()
        discover_moments_video.setOnCompletionListener {
            discover_moments_video.start()
        }
    }

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

六毛六66

你的鼓励是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值