实现新闻频道管理

第四节

主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(),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()
    var mOtherList = ArrayList()
    lateinit var mUserAdapter: ChannelAdapter
    lateinit var mOtherAdapter: ChannelAdapter

    var moreFlag = MutableLiveData()

    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 {

    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(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()
}

主布局:




    

        

        

        
    

    

        

            

            

        

        

        

        

        

        


    

效果图:

实现新闻频道管理_第1张图片  实现新闻频道管理_第2张图片

此外:我还对 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 : AppCompatActivity() {

    lateinit var mBinding: DB

    protected val mViewModel: VM by lazy {
        val kclazz = (GenericUtil.getClassType(this, BaseViewModel::class) as KClass).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  create(modelClass: Class): 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 

赞赏码,哈哈哈
实现新闻频道管理_第3张图片

你可能感兴趣的:(kotlin,移动开发)