实现新闻频道管理

Posted 天耀106

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实现新闻频道管理相关的知识,希望对你有一定的参考价值。

第四节

主activity
 

package com.tian.yao.four.channel

import android.util.Log
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.chad.library.adapter.base.BaseQuickAdapter
import com.chad.library.adapter.base.listener.OnItemClickListener
import com.tian.yao.BR
import com.tian.yao.R
import com.tian.yao.databinding.ActivityChannelBinding
import com.tian.yao.four.BaseActivity
import com.tian.yao.two.ToastUtils

class ChannelActivity : BaseActivity<ChannelViewModel, ActivityChannelBinding>(),OnItemClickListener

    /**
     * 动画时长
     */
    private val animDuration: Long = 300

    override fun layoutId(): Int 
        return R.layout.activity_channel
    

    override fun initVariableId(): Int 
        return BR.channelViewModel
    

    override fun initSetup() 
        mViewModel.initData()

        mBinding.apply 
            channelAction = mViewModel.channelAction

            gvUser.layoutManager= GridLayoutManager(this@ChannelActivity,4)
            gvUser.adapter = mViewModel.mUserAdapter
            mViewModel.mUserAdapter.setOnItemClickListener(this@ChannelActivity)

            gvOther.layoutManager= GridLayoutManager(this@ChannelActivity,4)
            gvOther.adapter = mViewModel.mOtherAdapter
            mViewModel.mOtherAdapter.setOnItemClickListener(this@ChannelActivity)

            mViewModel.moreFlag.observe(this@ChannelActivity,
                tvMore.visibility = if(it) View.VISIBLE else View.GONE
                gvOther.visibility = if(it) View.VISIBLE else View.GONE
                ivEdit.setImageResource(if(it) R.drawable.svg_ok else R.drawable.svg_edit)
            )
        
    

    override fun onItemClick(adapter: BaseQuickAdapter<*, *>, view: View, position: Int) 
        //判断是否是编辑态
        //如果是编辑态则点击的时候处理增加/删除操作
        //如果不是编辑态则弹出Toast提示,实际使用中可以换成频道详情页的跳转
        if (ChannelAdapter.isEdit()) 
            var currentView: RecyclerView //currentView表示当前被点击的对象
            var anotherView: RecyclerView //anotherView表示另一个对象
            if (adapter == mBinding.gvUser.adapter) 
                currentView = mBinding.gvUser
                anotherView = mBinding.gvOther
             else 
                currentView = mBinding.gvOther
                anotherView = mBinding.gvUser
            
            //计算起点,获取点击View的坐标
            var startPos = IntArray(2)
            var endPos = IntArray(2)
            view.getLocationInWindow(startPos)

            var currentAdapter = currentView.adapter as ChannelAdapter
            var anotherAdapter = anotherView.adapter as ChannelAdapter

            //标记点击的item待删除,并添加到anotherView中
            Log.i("wltest", "onItemClick: "+currentAdapter.getItem(position))
            anotherAdapter.setTranslating(true)
            anotherAdapter.add(currentAdapter.setRemove(position))
            var cloneView  = mViewModel.getCloneView(view)

            currentView.post(Runnable 
                (window.decorView as ViewGroup).addView(cloneView,ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT)
                var lastView = anotherView.getChildAt(anotherView.childCount - 1)
                lastView.getLocationInWindow(endPos)
                mViewModel.moveAnimation(cloneView, startPos, endPos, animDuration)
            )
         else 
            ToastUtils.showShortText(mViewModel.mUserList[position])
        
    

viewModel层:

package com.tian.yao.four.channel

import android.app.Application
import android.graphics.Bitmap
import android.graphics.Canvas
import android.view.View
import android.view.ViewGroup
import android.view.animation.Animation
import android.view.animation.TranslateAnimation
import android.widget.ImageView
import androidx.lifecycle.MutableLiveData
import com.tian.yao.four.BaseViewModel
import org.json.JSONObject
import java.nio.charset.Charset

class ChannelViewModel(application: Application) : BaseViewModel(application) 

    private val fileName="channel.json"
    var mUserList = ArrayList<String>()
    var mOtherList = ArrayList<String>()
    lateinit var mUserAdapter: ChannelAdapter
    lateinit var mOtherAdapter: ChannelAdapter

    var moreFlag = MutableLiveData<Boolean>()

    fun initData()
        initJsonData()
        initAdapter()
    

    private fun initJsonData()
        var inputStream = resources.assets.open(fileName)
        val length = inputStream.available()
        val buffer = ByteArray(length)
        inputStream.read(buffer)
        val result = String(buffer, Charset.defaultCharset())
        val jsonObject = JSONObject(result)
        val userArray = jsonObject.optJSONArray("user")
        val otherArray = jsonObject.optJSONArray("other")
        for (index in 1..userArray.length())
            mUserList.add( userArray.getString(index-1))
        
        for (index in 1..otherArray.length())
            mOtherList.add( otherArray.getString(index-1))
        
    

    private fun initAdapter()
        mUserAdapter = ChannelAdapter(true)
        mUserAdapter.setNewInstance(mUserList)
        mOtherAdapter = ChannelAdapter(false)
        mOtherAdapter.setNewInstance(mOtherList)
    

    val channelAction=object :ChannelAction

        override fun edit() 
            toggleEditState()
        
    

    private fun toggleEditState() 
        val isEdit = ChannelAdapter.isEdit()
        ChannelAdapter.setEdit(!isEdit)
        moreFlag.value= !isEdit
        mUserAdapter.notifyDataSetChanged()
        mOtherAdapter.notifyDataSetChanged()
    

    /**
     * 移动动画
     * @param moveView View 移动的目标View
     * @param startPos FloatArray 起点坐标
     * @param endPos FloatArray 终点坐标
     * @param duration Long 动画时长
     */
    fun moveAnimation(moveView: View, startPos: IntArray, endPos: IntArray, duration: Long) 
        //1、创建动画,设置起点和终点的坐标
        var animation = TranslateAnimation(startPos[0].toFloat(), endPos[0].toFloat(),
            startPos[1].toFloat(), endPos[1].toFloat()
        )
        //2、设置动画时长
        animation.duration = duration
        //3、设置不停留
        animation.fillAfter = false
        //4、设置监听器
        animation.setAnimationListener(object : Animation.AnimationListener 
            override fun onAnimationStart(animation: Animation?) 
            

            override fun onAnimationEnd(animation: Animation?) 
                (moveView.parent as ViewGroup).removeView(moveView)
                resetAdapter()
            

            override fun onAnimationRepeat(animation: Animation?) 
            
        )
        //5、启动动画
        moveView.startAnimation(animation)

    

    private fun resetAdapter() 
        mUserAdapter.setTranslating(false)
        mOtherAdapter.setTranslating(false)
        mUserAdapter.remove()
        mOtherAdapter.remove()
    

    fun getCloneView(view: View): ImageView 
        val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)
        val canvas = Canvas(bitmap)
        view.draw(canvas)
        var imageView = ImageView(view.context)
        imageView.setImageBitmap(bitmap)
        return imageView
    


适配器:

package com.tian.yao.four.channel

import android.widget.TextView
import androidx.core.content.ContextCompat
import com.chad.library.adapter.base.BaseQuickAdapter
import com.chad.library.adapter.base.viewholder.BaseViewHolder
import com.tian.yao.R

class ChannelAdapter : BaseQuickAdapter<String, BaseViewHolder> 

    private var mReadyToRemove: Int = -1 //标记预备删除的元素序号
    private var mIsUser: Boolean = true
    private var mAnimState = AnimState.IDEL

    constructor(isUser: Boolean) : super(R.layout.adapter_channel_item) 
        mIsUser = isUser
    

    /**
     * 动画状态枚举,用于对不通的动画状态进行处理,当前只支持空闲和移动
     * 目前看也可以用boolean,enum是为了后续扩展
     */
    enum class AnimState 
        IDEL,
        TRANSLATING
    

    companion object 
        private var mIsEditState: Boolean = false

        fun setEdit(isEdit: Boolean) 
            mIsEditState = isEdit
        

        fun isEdit(): Boolean 
            return mIsEditState
        
    

    fun add(channelName: String) 
        data.add(channelName)
        notifyDataSetChanged()
    

    /**
     * 添加删除标记
     * @param index Int 待删除的序号
     */
    fun setRemove(index: Int): String 
        mReadyToRemove = index
        notifyDataSetChanged()
        return data[index]
    

    fun setTranslating(translating: Boolean) 
        mAnimState = if (translating) AnimState.TRANSLATING else AnimState.IDEL
    

    fun remove() 
        if (mReadyToRemove > 0 && mReadyToRemove < data.size) 
            removeAt(mReadyToRemove)
        
        mReadyToRemove = -1
        notifyDataSetChanged()
    


    override fun convert(holder: BaseViewHolder, item: String) 
        var tvItem = holder.getView<TextView>(R.id.tv_item)
        tvItem.text = item
        if (mIsEditState) 
            holder.setVisible(R.id.iv_icon, true)
            holder.setImageDrawable(
                R.id.iv_icon,
                if (mIsUser)
                    ContextCompat.getDrawable(context, R.drawable.svg_subtraction)
                else
                    ContextCompat.getDrawable(context, R.drawable.svg_add)
            )
         else 
            holder.setVisible(R.id.iv_icon, false)
        
        // 1、第一个条件处理currentView的状态
        // 2、第二个条件处理anotherView的状态

        if (mReadyToRemove == getItemPosition(item) || (mAnimState == AnimState.TRANSLATING && getItemPosition(item) == itemCount - 1)) 
            tvItem.text = ""
            holder.setVisible(R.id.iv_icon, false)
         else 
            tvItem.text = item
        
    

点击Action接口:

package com.tian.yao.four.channel

interface ChannelAction 

    fun edit()

主布局:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

        <variable
            name="channelViewModel"
            type="com.tian.yao.four.channel.ChannelViewModel" />

        <variable
            name="channelAction"
            type="com.tian.yao.four.channel.ChannelAction" />

        <import type="android.view.View"/>
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="10dp"
            android:layout_marginTop="15dp"
            android:layout_marginEnd="10dp">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="我的频道"
                android:textSize="18sp" />

            <ImageView
                android:id="@+id/iv_edit"
                android:layout_width="22dp"
                android:layout_height="22dp"
                android:layout_alignParentEnd="true"
                android:onClick="@v->channelAction.edit()"
                android:padding="2dp"
                app:srcCompat="@drawable/svg_edit" />

        </RelativeLayout>

        <View
            android:layout_width="match_parent"
            android:layout_height="0.5dp"
            android:layout_marginTop="10dp"
            android:layout_marginBottom="15dp" />

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/gv_user"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="14dp"
            android:layout_marginEnd="14dp"
            android:gravity="center"
            android:horizontalSpacing="14dp"
            android:numColumns="4"
            android:scrollbars="vertical"
            android:stretchMode="columnWidth"
            android:verticalSpacing="14dp" />

        <View
            android:layout_width="match_parent"
            android:layout_height="0.5dp"
            android:layout_marginTop="10dp" />

        <TextView
            android:id="@+id/tv_more"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="10dp"
            android:layout_marginEnd="10dp"
            android:layout_marginBottom="15dp"
            android:text="更多频道"
            android:visibility="gone"
            android:textSize="18sp" />

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/gv_other"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="14dp"
            android:layout_marginEnd="14dp"
            android:gravity="center"
            android:visibility="gone"
            android:horizontalSpacing="14dp"
            android:numColumns="4"
            android:scrollbars="vertical"
            android:stretchMode="columnWidth" />


    </LinearLayout>
</layout>

效果图:

  

此外:我还对 BaseActivity做了自己的初步封装,可以一步步学习~

package com.tian.yao.four

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.lifecycle.ViewModelProvider
import kotlin.reflect.KClass

abstract class BaseActivity<VM : BaseViewModel, DB : ViewDataBinding> : AppCompatActivity() 

    lateinit var mBinding: DB

    protected val mViewModel: VM by lazy 
        val kclazz = (GenericUtil.getClassType(this, BaseViewModel::class) as KClass<VM>).java
        val viewModel = ViewModelProvider(this,ViewModelFactory(application)).get(kclazz)
        if (initVariableId() > 0) 
            mBinding.setVariable(initVariableId(), viewModel)
        
        viewModel
    

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        mBinding = DataBindingUtil.setContentView(this, layoutId())
        mBinding.lifecycleOwner = this
        initSetup()
    

    abstract fun layoutId(): Int

    abstract fun initVariableId(): Int

    abstract fun initSetup()

BaseViewModel层

package com.tian.yao.four

import android.app.Application
import android.content.res.Resources
import androidx.lifecycle.AndroidViewModel

open class BaseViewModel(application: Application) : AndroidViewModel(application) 

    protected val resources: Resources = application.resources

ViewModelFactory

package com.tian.yao.four

import android.app.Application
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import java.lang.reflect.InvocationTargetException

class ViewModelFactory(private val application: Application) : ViewModelProvider.Factory 

    override fun <T : ViewModel?> create(modelClass: Class<T>): T 
        if (modelClass.isAssignableFrom(BaseViewModel::class.java)) 
            return BaseViewModel(application) as T
        
        //反射动态实例化ViewModel
        return try 
            val className = modelClass.canonicalName
            val classViewModel = Class.forName(className)
            val cons = classViewModel.getConstructor(Application::class.java)
            val viewModel = cons.newInstance(application) as ViewModel
            viewModel as T
         catch (e: ClassNotFoundException) 
            e.printStackTrace()
            throw java.lang.IllegalArgumentException("Unknown ViewModel class: " + modelClass.name)
         catch (e: IllegalAccessException) 
            e.printStackTrace()
            throw java.lang.IllegalArgumentException("Unknown ViewModel class: " + modelClass.name)
         catch (e: InstantiationException) 
            e.printStackTrace()
            throw java.lang.IllegalArgumentException("Unknown ViewModel class: " + modelClass.name)
         catch (e: NoSuchMethodException) 
            e.printStackTrace()
            throw java.lang.IllegalArgumentException("Unknown ViewModel class: " + modelClass.name)
         catch (e: InvocationTargetException) 
            e.printStackTrace()
            throw java.lang.IllegalArgumentException("Unknown ViewModel class: " + modelClass.name)
        
    

工具类

package com.tian.yao.four

import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import kotlin.reflect.KClass

object GenericUtil 

    fun getClassType(obj: Any, destClz: KClass<*>) = getClassType(obj::class.java, destClz.java)

    private fun getClassType(clz: Class<*>, destClz: Class<*>): KClass<*>? 
        var c = clz
        var genType: Type?

        while (c.superclass != null) 
            genType = c.genericSuperclass
            if (genType !is ParameterizedType) 
                c = c.superclass as Class<*>
                continue
             else 
                val types = genType.actualTypeArguments
                for (type in types) 
                    val tClz = type as Class<*>
                    return if (destClz.isAssignableFrom(tClz)) 
                        type.kotlin
                     else 
                        continue
                    
                
                break
            
        

        return null
    


代码链接:

链接: https://pan.baidu.com/s/1aqpcuhVgIkCr2rfnDV_6ng 提取码: rrea 

赞赏码,哈哈哈

以上是关于实现新闻频道管理的主要内容,如果未能解决你的问题,请参考以下文章

实现新闻频道管理

实现新闻频道管理

实现新闻频道管理

iOS 利用UICollectionView拖拽排序 实现的仿照腾讯新闻频道管理功能 XLChannelControl

高仿新闻类APP频道管理功能,ItemTouchHelper的实践

新闻网站如何设计数据库