├── LICENSE ├── README.md ├── Untitled Document.md.pdf └── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src ├── androidTest └── java │ └── com │ └── example │ └── zzh │ └── channelmanagedemo_kotlin │ └── ExampleInstrumentedTest.kt ├── main ├── AndroidManifest.xml ├── assets │ └── ezgif-5-d492977e87_kotlin.gif ├── java │ └── com │ │ └── example │ │ └── zzh │ │ └── channelmanagedemo_kotlin │ │ ├── ChannelAdapter.kt │ │ ├── ChannelBean.kt │ │ ├── Ex.kt │ │ ├── GridSpacingItemDecoration.kt │ │ ├── ItemDragCallback.kt │ │ └── MainActivity.kt └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ ├── ic_launcher_background.xml │ └── icon_close.png │ ├── layout │ ├── activity_main.xml │ ├── adapter_channel.xml │ ├── adapter_more_channel.xml │ ├── adapter_tab.xml │ └── adapter_title.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml └── test └── java └── com └── example └── zzh └── channelmanagedemo_kotlin └── ExampleUnitTest.kt /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 zzh12138 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ChannelManageDemo_Kotlin 2 | https://www.jianshu.com/p/57324eb516df 3 | ###### ChannelManageDemo by kotlin 4 | ### 高仿腾讯新闻频道管理页面 5 | #### 使用kotlin语言编写,用recyclerView+ItemTouchHelper实现 6 | ![avatar](https://raw.githubusercontent.com/zzh12138/ChannelManageDemo_Kotlin/master/app/src/main/assets/ezgif-5-d492977e87_kotlin.gif) 7 | ## License 8 | MIT 9 | -------------------------------------------------------------------------------- /Untitled Document.md.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/ChannelManageDemo_Kotlin/5019cc796c65b42c7f4fb7b62088135b55e34a48/Untitled Document.md.pdf -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | apply plugin: 'kotlin-android-extensions' 6 | 7 | android { 8 | compileSdkVersion 26 9 | defaultConfig { 10 | applicationId "com.example.zzh.channelmanagedemo_kotlin" 11 | minSdkVersion 15 12 | targetSdkVersion 26 13 | versionCode 1 14 | versionName "1.0" 15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | implementation fileTree(dir: 'libs', include: ['*.jar']) 27 | implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" 28 | implementation 'com.android.support:appcompat-v7:26.1.0' 29 | compile 'com.android.support:recyclerview-v7:26.1.0' 30 | implementation 'com.android.support.constraint:constraint-layout:1.0.2' 31 | testImplementation 'junit:junit:4.12' 32 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 33 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 34 | } 35 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/example/zzh/channelmanagedemo_kotlin/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.zzh.channelmanagedemo_kotlin 2 | 3 | import android.support.test.InstrumentationRegistry 4 | import android.support.test.runner.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getTargetContext() 22 | assertEquals("com.example.zzh.channelmanagedemo_kotlin", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/assets/ezgif-5-d492977e87_kotlin.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/ChannelManageDemo_Kotlin/5019cc796c65b42c7f4fb7b62088135b55e34a48/app/src/main/assets/ezgif-5-d492977e87_kotlin.gif -------------------------------------------------------------------------------- /app/src/main/java/com/example/zzh/channelmanagedemo_kotlin/ChannelAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.example.zzh.channelmanagedemo_kotlin 2 | 3 | import android.animation.Animator 4 | import android.animation.AnimatorListenerAdapter 5 | import android.animation.AnimatorSet 6 | import android.animation.ObjectAnimator 7 | import android.content.Context 8 | import android.graphics.Typeface 9 | import android.support.v7.widget.RecyclerView 10 | import android.view.LayoutInflater 11 | import android.view.View 12 | import android.view.ViewGroup 13 | import android.view.ViewTreeObserver 14 | import android.widget.ImageView 15 | import android.widget.LinearLayout 16 | import android.widget.TextView 17 | import java.util.* 18 | 19 | /** 20 | * Created by zhangzhihao on 2018/3/6. 21 | */ 22 | class ChannelAdapter(var mContext: Context, var mList: ArrayList, var recommendList: ArrayList, var cityList: ArrayList) 23 | : RecyclerView.Adapter() { 24 | var selectedSize: Int = 0 //已选 25 | var fixSize: Int = 0 //固定频道数目 26 | var isRecommend: Boolean = true //当前是否显示推荐频道 27 | var mLeft: Int = -1 //推荐频道蓝色线条距离屏幕左边的距离 28 | var mRight: Int = -1 //城市频道蓝色线条距离屏幕左边的距离 29 | var mTabY: Int = 0 //tab距离parent的Y的距离 30 | var onItemRangeChangeListener: OnItemRangeChangeListener? = null 31 | 32 | override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder { 33 | val view = LayoutInflater.from(mContext).inflate(viewType, parent, false) 34 | return when (viewType) { 35 | R.layout.adapter_channel -> ChannelHolder(view) 36 | R.layout.adapter_more_channel -> MoreChannelHolder(view) 37 | R.layout.adapter_tab -> TabHolder(view) 38 | else -> TitleHolder(view) 39 | } 40 | } 41 | 42 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder?, position: Int) { 43 | if (holder is ChannelHolder) { 44 | setChannel(holder, position) 45 | } else if (holder is TabHolder) { 46 | setTab(holder) 47 | } 48 | 49 | } 50 | 51 | override fun getItemCount(): Int { 52 | return mList.size 53 | } 54 | 55 | override fun getItemViewType(position: Int): Int { 56 | return mList[position].layoutId 57 | } 58 | 59 | private fun setChannel(holder: ChannelHolder, position: Int) { 60 | holder.name.text = mList[position].name 61 | holder.name.setOnClickListener { 62 | if (holder.layoutPosition < selectedSize + 1) { 63 | //tab上面的 点击移除 64 | if (holder.layoutPosition > fixSize) { 65 | removeFromSelected(holder) 66 | } 67 | } else { 68 | //tab下面的 点击添加到已选频道 69 | selectedSize++ 70 | itemMove(holder.layoutPosition, selectedSize) 71 | notifyItemChanged(selectedSize) 72 | //刷新itemDecoration 73 | onItemRangeChangeListener?.let { onItemRangeChangeListener!!.refreshItemDecoration() } 74 | } 75 | } 76 | holder.name.setOnLongClickListener { true } 77 | holder.delete.setOnClickListener { removeFromSelected(holder) } 78 | //tab下面及固定频道不显示删除按钮 79 | if (position - 1 < fixSize || position > selectedSize) { 80 | holder.delete.visibility = View.GONE 81 | } else { 82 | holder.delete.visibility = View.VISIBLE 83 | } 84 | } 85 | 86 | private fun setTab(holder: TabHolder) { 87 | val params = holder.indicator.layoutParams as LinearLayout.LayoutParams 88 | //计算蓝色线条位置 89 | if (mLeft == -1) { 90 | holder.recommend.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener { 91 | override fun onPreDraw(): Boolean { 92 | val layout = holder.recommend.layout 93 | //textView左边距离+第一个文字绘制位置-padding 94 | mLeft = (holder.recommend.left + layout.getPrimaryHorizontal(0) - mContext.dip2px(10f)).toInt() 95 | params.leftMargin = mLeft; 96 | holder.indicator.layoutParams = params 97 | holder.recommend.viewTreeObserver.removeOnPreDrawListener(this) 98 | return true 99 | } 100 | }) 101 | } 102 | 103 | holder.city.setOnClickListener { 104 | if (isRecommend) { 105 | holder.city.typeface = Typeface.DEFAULT_BOLD 106 | holder.recommend.typeface = Typeface.DEFAULT 107 | if (mRight == -1) { 108 | mRight = (mLeft + holder.city.left - mContext.dip2px(10f)).toInt() 109 | } 110 | params.leftMargin = mRight 111 | isRecommend = false 112 | recommendList.clear() 113 | recommendList.addAll(mList.subList(selectedSize + 2, mList.size)) 114 | mList.removeAll(recommendList) 115 | mList.addAll(cityList) 116 | notifyDataSetChanged() 117 | } 118 | } 119 | 120 | holder.recommend.setOnClickListener { 121 | if (!isRecommend) { 122 | holder.city.typeface = Typeface.DEFAULT 123 | holder.recommend.typeface = Typeface.DEFAULT_BOLD 124 | params.leftMargin = mLeft 125 | isRecommend = true 126 | cityList.clear() 127 | cityList.addAll(mList.subList(selectedSize + 2, mList.size)) 128 | mList.removeAll(cityList) 129 | mList.addAll(recommendList) 130 | notifyDataSetChanged() 131 | } 132 | } 133 | holder.itemView.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener { 134 | override fun onPreDraw(): Boolean { 135 | mTabY = holder.itemView.top 136 | return true 137 | } 138 | 139 | }) 140 | 141 | } 142 | 143 | private fun removeFromSelected(holder: ChannelHolder) { 144 | holder.delete.visibility = View.GONE 145 | val position = holder.layoutPosition 146 | val bean = mList[position] 147 | if ((isRecommend && bean.isRecommend) || (!isRecommend && !bean.isRecommend)) { 148 | //移除的频道属于当前tab显示的频道,直接调用系统方法移除 149 | itemMove(position, selectedSize + 1) 150 | notifyItemRangeChanged(selectedSize + 1, 1) 151 | //刷新itemDecoration 152 | onItemRangeChangeListener?.let { onItemRangeChangeListener!!.refreshItemDecoration() } 153 | } else { 154 | //不属于当前tab显示的频道 155 | removeAnimation(holder.itemView, (if (isRecommend) mRight else mLeft).toFloat(), mTabY.toFloat(), position) 156 | } 157 | selectedSize-- 158 | } 159 | 160 | private fun removeAnimation(view: View, x: Float, y: Float, position: Int) { 161 | val fromX = view.left 162 | val fromY = view.top 163 | val animatorX = ObjectAnimator.ofFloat(view, "translationX", 0f, x - fromX) 164 | val animatorY = ObjectAnimator.ofFloat(view, "translationY", 0f, y - fromY) 165 | val alpha = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f) 166 | val set = AnimatorSet() 167 | set.playTogether(animatorX, animatorY, alpha) 168 | set.duration = 350 169 | set.addListener(object : Animator.AnimatorListener { 170 | override fun onAnimationRepeat(animation: Animator?) { 171 | } 172 | 173 | override fun onAnimationCancel(animation: Animator?) { 174 | } 175 | 176 | override fun onAnimationStart(animation: Animator?) { 177 | } 178 | 179 | override fun onAnimationEnd(animation: Animator?) { 180 | if (isRecommend) { 181 | cityList.add(0, mList[position]) 182 | } else { 183 | recommendList.add(0, mList[position]) 184 | } 185 | mList.removeAt(position) 186 | notifyItemRemoved(position) 187 | onItemRangeChangeListener?.let { onItemRangeChangeListener!!.refreshItemDecoration() } 188 | //这里需要重置view的属性 189 | resetView(view, x - fromX, y - fromY) 190 | } 191 | }) 192 | set.start() 193 | } 194 | 195 | private fun resetView(view: View, toX: Float, toY: Float) { 196 | val animatorX = ObjectAnimator.ofFloat(view, "translationX", -toX, 0f) 197 | val animatorY = ObjectAnimator.ofFloat(view, "translationY", -toY, 0f) 198 | val alpha = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f) 199 | val set = AnimatorSet() 200 | set.playTogether(animatorX, animatorY, alpha) 201 | set.duration = 0 202 | set.startDelay = 5 203 | set.start() 204 | } 205 | 206 | fun itemMove(fromPosition: Int, toPosition: Int) { 207 | if (fromPosition < toPosition) { 208 | for (i in fromPosition until toPosition) { 209 | Collections.swap(mList, i, i + 1) 210 | } 211 | } else { 212 | for (i in fromPosition downTo toPosition + 1) { 213 | Collections.swap(mList, i, i - 1) 214 | } 215 | } 216 | notifyItemMoved(fromPosition, toPosition) 217 | } 218 | 219 | class ChannelHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 220 | val name: TextView = itemView.findViewById(R.id.channel_name) 221 | val delete: ImageView = itemView.findViewById(R.id.channel_delete) 222 | } 223 | 224 | class TabHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 225 | val recommend: TextView = itemView.findViewById(R.id.recommend_channel) 226 | val city: TextView = itemView.findViewById(R.id.city_channel) 227 | val indicator: View = itemView.findViewById(R.id.indicator) 228 | } 229 | 230 | class MoreChannelHolder(itemView: View) : RecyclerView.ViewHolder(itemView) 231 | class TitleHolder(itemView: View) : RecyclerView.ViewHolder(itemView) 232 | 233 | interface OnItemRangeChangeListener { 234 | fun refreshItemDecoration() 235 | } 236 | 237 | } 238 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/zzh/channelmanagedemo_kotlin/ChannelBean.kt: -------------------------------------------------------------------------------- 1 | package com.example.zzh.channelmanagedemo_kotlin 2 | 3 | /** 4 | * Created by zhangzhihao on 2018/3/6. 5 | */ 6 | class ChannelBean(var name:String ,var spanSize:Int ,var layoutId: Int,var isRecommend: Boolean){ 7 | } 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/zzh/channelmanagedemo_kotlin/Ex.kt: -------------------------------------------------------------------------------- 1 | package com.example.zzh.channelmanagedemo_kotlin 2 | 3 | import android.content.Context 4 | 5 | /** 6 | * Created by zhangzhihao on 2018/3/6. 7 | */ 8 | fun Context.dip2px(value:Float): Float { 9 | val scale=this.resources.displayMetrics.density 10 | return value*scale+0.5f 11 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/zzh/channelmanagedemo_kotlin/GridSpacingItemDecoration.kt: -------------------------------------------------------------------------------- 1 | package com.example.zzh.channelmanagedemo_kotlin 2 | 3 | import android.graphics.Rect 4 | import android.support.v7.widget.RecyclerView 5 | import android.view.View 6 | 7 | /** 8 | * Created by zhangzhihao on 2018/3/7. 9 | */ 10 | class GridSpacingItemDecoration(var spanCount: Int, var spacing: Int, var includeEdge: Boolean) : RecyclerView.ItemDecoration() { 11 | private var tabPosition = 0 12 | override fun getItemOffsets(outRect: Rect?, view: View?, parent: RecyclerView?, state: RecyclerView.State?) { 13 | var position = parent!!.getChildAdapterPosition(view) 14 | if (position > 0) { 15 | val id = parent.adapter.getItemViewType(position) 16 | if (id == R.layout.adapter_tab) { 17 | tabPosition = position 18 | } 19 | if (id == R.layout.adapter_channel) { 20 | if (position <= tabPosition) { 21 | position-- 22 | } else { 23 | position -= (tabPosition + 1) 24 | } 25 | val column = position % spanCount 26 | if (includeEdge) { 27 | outRect!!.left = spacing - column * spacing / spanCount 28 | outRect.right = (column + 1) * spacing / spanCount 29 | if(position=spanCount){ 37 | outRect.top=20 38 | } 39 | } 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/zzh/channelmanagedemo_kotlin/ItemDragCallback.kt: -------------------------------------------------------------------------------- 1 | package com.example.zzh.channelmanagedemo_kotlin 2 | 3 | import android.graphics.Canvas 4 | import android.graphics.Color 5 | import android.graphics.DashPathEffect 6 | import android.graphics.Paint 7 | import android.support.v7.widget.RecyclerView 8 | import android.support.v7.widget.helper.ItemTouchHelper 9 | import android.support.v7.widget.helper.ItemTouchHelper.ACTION_STATE_DRAG 10 | import android.view.View 11 | 12 | /** 13 | * Created by zhangzhihao on 2018/3/7. 14 | */ 15 | class ItemDragCallback(var mAdapter: ChannelAdapter, var mPadding: Int) : ItemTouchHelper.Callback() { 16 | private val mPaint: Paint = Paint() 17 | 18 | init { 19 | mPaint.color = Color.GRAY 20 | mPaint.isAntiAlias = true 21 | mPaint.strokeWidth = 1f 22 | mPaint.style = Paint.Style.STROKE 23 | val pathEffect = DashPathEffect(FloatArray(2, { 5f }), 5f) 24 | mPaint.pathEffect = pathEffect 25 | } 26 | 27 | override fun getMovementFlags(recyclerView: RecyclerView?, viewHolder: RecyclerView.ViewHolder?): Int { 28 | //固定位置及tab下面的channel不能拖动 29 | val position = viewHolder!!.layoutPosition 30 | if (position < mAdapter.fixSize + 1 || position > mAdapter.selectedSize) { 31 | return makeMovementFlags(0, 0) 32 | } 33 | val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT 34 | return makeMovementFlags(dragFlags, 0) 35 | } 36 | 37 | override fun onMove(recyclerView: RecyclerView?, viewHolder: RecyclerView.ViewHolder?, target: RecyclerView.ViewHolder?): Boolean { 38 | val fromPosition = viewHolder!!.layoutPosition //拖动的位置 39 | val toPosition = target!!.layoutPosition //释放的位置 40 | //固定位置及tab下面的channel不能移动 41 | if (toPosition < mAdapter.fixSize + 1 || toPosition > mAdapter.selectedSize) { 42 | return false 43 | } 44 | mAdapter.itemMove(fromPosition, toPosition) 45 | return true 46 | } 47 | 48 | override fun onSwiped(viewHolder: RecyclerView.ViewHolder?, direction: Int) { 49 | //滑动重写这里 50 | } 51 | 52 | override fun onChildDrawOver(c: Canvas?, recyclerView: RecyclerView?, viewHolder: RecyclerView.ViewHolder?, dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean) { 53 | super.onChildDrawOver(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive) 54 | if (dX != 0f && dY != 0f || isCurrentlyActive) { 55 | //长按拖拽时底部绘制一个虚线矩形 56 | c!!.drawRect(viewHolder!!.itemView.left.toFloat(), (viewHolder.itemView.top - mPadding).toFloat(), viewHolder.itemView.right.toFloat(), viewHolder.itemView.bottom.toFloat(), mPaint) 57 | } 58 | } 59 | 60 | override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { 61 | super.onSelectedChanged(viewHolder, actionState) 62 | if (actionState == ACTION_STATE_DRAG) { 63 | //长按时调用 设置颜色 阴影 64 | val holder = viewHolder as ChannelAdapter.ChannelHolder 65 | holder.name.setBackgroundColor(Color.parseColor("#FDFDFE")) 66 | holder.delete.visibility= View.GONE 67 | holder.name.elevation=5f 68 | } 69 | } 70 | 71 | override fun clearView(recyclerView: RecyclerView?, viewHolder: RecyclerView.ViewHolder?) { 72 | super.clearView(recyclerView, viewHolder) 73 | //重置view 74 | val holder=viewHolder as ChannelAdapter.ChannelHolder 75 | holder.name.setBackgroundColor(Color.parseColor("#f0f0f0")) 76 | holder.delete.visibility=View.VISIBLE 77 | holder.name.elevation=0f 78 | } 79 | 80 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/zzh/channelmanagedemo_kotlin/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.zzh.channelmanagedemo_kotlin 2 | 3 | import android.content.Context 4 | import android.support.v7.app.AppCompatActivity 5 | import android.os.Bundle 6 | import android.support.v7.widget.DefaultItemAnimator 7 | import android.support.v7.widget.GridLayoutManager 8 | import android.support.v7.widget.helper.ItemTouchHelper 9 | import android.view.WindowManager 10 | import kotlinx.android.synthetic.main.activity_main.* 11 | 12 | class MainActivity : AppCompatActivity(), ChannelAdapter.OnItemRangeChangeListener { 13 | 14 | 15 | private val select = arrayOf("要闻", "体育", "新时代", "汽车", "时尚", "国际", "电影", "财经", "游戏", "科技", "房产", "政务", "图片", "独家") 16 | private val recommend = arrayOf("娱乐", "军事", "文化", "视频", "股票", "动漫", "理财", "电竞", "数码", "星座", "教育", "美容", "旅游") 17 | private val city = arrayOf("重庆", "深圳", "汕头", "东莞", "佛山", "江门", "湛江", "惠州", "中山", "揭阳", "韶关", "茂名", "肇庆", "梅州", "汕尾", "河源", "云浮", "四川") 18 | val mList = arrayListOf() 19 | var mAdapter: ChannelAdapter? = null 20 | override fun onCreate(savedInstanceState: Bundle?) { 21 | super.onCreate(savedInstanceState) 22 | setContentView(R.layout.activity_main) 23 | val manager = GridLayoutManager(this, 4) 24 | manager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { 25 | override fun getSpanSize(position: Int): Int { 26 | return mList[position].spanSize 27 | } 28 | } 29 | recyclerView.layoutManager = manager 30 | val animator = DefaultItemAnimator() 31 | animator.moveDuration = 300 //设置动画时间 32 | animator.removeDuration = 0 33 | recyclerView.itemAnimator = animator 34 | createData() 35 | recyclerView.adapter = mAdapter 36 | val m = getSystemService(Context.WINDOW_SERVICE) as WindowManager 37 | val spacing = ((m.defaultDisplay.width - this.dip2px(70f) * 4) / 5).toInt() 38 | recyclerView.addItemDecoration(GridSpacingItemDecoration(4, spacing, true)) 39 | val callback = ItemDragCallback(mAdapter!!, 2) 40 | val helper = ItemTouchHelper(callback) 41 | helper.attachToRecyclerView(recyclerView) 42 | } 43 | 44 | private fun createData() { 45 | mList.add(ChannelBean("", 4, R.layout.adapter_title, false)) 46 | select.mapTo(mList) { ChannelBean(it, 1, R.layout.adapter_channel, true) } 47 | mList.add(ChannelBean("", 4, R.layout.adapter_tab, false)) 48 | val recommendList = arrayListOf() 49 | recommend.mapTo(recommendList) { ChannelBean(it, 1, R.layout.adapter_channel, true) } 50 | val cityList = arrayListOf() 51 | city.mapTo(cityList) { ChannelBean(it, 1, R.layout.adapter_channel, false) } 52 | cityList.add(ChannelBean("", 4, R.layout.adapter_more_channel, false)) 53 | mList.addAll(recommendList) 54 | mAdapter = ChannelAdapter(this, mList, recommendList, cityList) 55 | mAdapter!!.fixSize = 1 56 | mAdapter!!.selectedSize = select.size 57 | mAdapter!!.onItemRangeChangeListener = this 58 | } 59 | 60 | override fun refreshItemDecoration() { 61 | recyclerView.invalidateItemDecorations() 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/ChannelManageDemo_Kotlin/5019cc796c65b42c7f4fb7b62088135b55e34a48/app/src/main/res/drawable/icon_close.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 17 | 18 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/adapter_channel.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 17 | 18 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/adapter_more_channel.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/adapter_tab.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 24 | 25 | 32 | 33 | 34 | 39 | 40 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/res/layout/adapter_title.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/ChannelManageDemo_Kotlin/5019cc796c65b42c7f4fb7b62088135b55e34a48/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/ChannelManageDemo_Kotlin/5019cc796c65b42c7f4fb7b62088135b55e34a48/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/ChannelManageDemo_Kotlin/5019cc796c65b42c7f4fb7b62088135b55e34a48/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/ChannelManageDemo_Kotlin/5019cc796c65b42c7f4fb7b62088135b55e34a48/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/ChannelManageDemo_Kotlin/5019cc796c65b42c7f4fb7b62088135b55e34a48/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/ChannelManageDemo_Kotlin/5019cc796c65b42c7f4fb7b62088135b55e34a48/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/ChannelManageDemo_Kotlin/5019cc796c65b42c7f4fb7b62088135b55e34a48/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/ChannelManageDemo_Kotlin/5019cc796c65b42c7f4fb7b62088135b55e34a48/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/ChannelManageDemo_Kotlin/5019cc796c65b42c7f4fb7b62088135b55e34a48/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzh12138/ChannelManageDemo_Kotlin/5019cc796c65b42c7f4fb7b62088135b55e34a48/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ChannelManageDemo_Kotlin 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/example/zzh/channelmanagedemo_kotlin/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.zzh.channelmanagedemo_kotlin 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } 18 | --------------------------------------------------------------------------------