尝试用kotlin做一个app(二)

蹲街弑〆低调 提交于 2020-02-23 00:35:48

导航条

我想实现的效果是这样的

 

 类似于ViewPager的效果,子类导航页面可以滑动,当滑动某个子类导航页面,导航线会平滑地向父类导航移动

·添加布局

<!--导航分类:编程语言/技术文档/源码下载-->
    <LinearLayout
        android:id="@+id/homepage_nav"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_below="@+id/vp_homePageAd"
        >
        <TextView
            android:id="@+id/homepage_nav_prog"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="编程语言"
            android:textSize="17dp"
            android:textStyle="bold"
            android:layout_weight="1"
            android:gravity="center"
            />
        <TextView
            android:id="@+id/homepage_nav_doc"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="技术文档"
            android:textStyle="bold"
            android:layout_weight="1"
            android:gravity="center"
            />
        <TextView
            android:id="@+id/homepage_nav_source"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="源码下载"
            android:textStyle="bold"
            android:layout_weight="1"
            android:gravity="center"
            />
    </LinearLayout>
<!-- 两条线-->
    <View
        android:id="@+id/homepage_nav_bottomLine"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_below="@+id/homepage_nav"
        android:background="#88888888"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"/>
    <!-- 移动的线-->
    <View
        android:id="@+id/homepage_nav_moveLine"
        android:layout_width="70dp"
        android:layout_height="3dp"
        android:layout_alignBottom="@+id/homepage_nav_bottomLine"
        android:background="#000000" />
<!--导航分类子类, 放在ViewPager中-->
    <androidx.viewpager.widget.ViewPager
        android:id="@+id/vp_homePageNav"
        android:layout_width="match_parent"
        android:layout_height="200px"
        android:layout_below="@+id/homepage_nav_moveLine"
        android:layout_marginTop="20px"/>

</RelativeLayout>

·设置ViewPager的滑动页面

页面有若干item组成,每个item按样式是一张图片,下面加文字。那据说有一种比较高级的实现方法,就是外边一个TextView,里面添加图片和文字。TextView里面加图片简单,只要添加android:drawableTop="icon的ID",但是这有一个问题,在尝试用drable.setBounds设置图片的宽高的时候,无效。

这篇文章解决TextView drawableLeft左侧图片大小不可控的问题,提供了一个思路,即自定义一个ImageTextView控件,继承自TextView,重写基类的方法,实现设置往里添加图片的宽,高,位置等。我把java转换成kotlin,再加了一些注释。原程序有bug可以看原文下面的评论。原文还引入了一个DensityUtil的类,作用是实现px和pd的转换,我注释起来了

·在values下添加attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="ImageTextView">
        <attr name="drawable" format="reference"/>
        <attr name="drawableWidth" format="dimension"/>
        <attr name="drawableHeight" format="dimension"/>
        <attr name="postion" format="integer"/>
    </declare-styleable>
</resources>

·新建ImageTextView类

class ImageTextView: TextView {
    private var mDrawable: Drawable?=null  //资源id
    private var mScaleWidth:Int?=null    //设置图片宽
    private var mScaleHeight:Int?=null   //设置图片高
    private var mPosition:Int?=null     //设置图片位置

    //初始化基类
    constructor(context: Context):super(context){
    }
    constructor(context: Context, attrs: AttributeSet):super(context,attrs){
        init(context,attrs);
    }
    constructor(context: Context, attrs: AttributeSet, defStyleAttr:Int):super(context, attrs, defStyleAttr){
        init(context,attrs);
    }

    //初始化获得xml文件中设置的属性
    protected fun init(context: Context, attrs: AttributeSet){
        //obtainStyledAttributes获得style中指定资源的属性值,返回typeArray
        var typeArray=context.obtainStyledAttributes(attrs, R.styleable.ImageTextView)

        mDrawable=typeArray.getDrawable(R.styleable.ImageTextView_drawable)
        println("在初始化中!!!!!!!")
        println(mDrawable)

        //getDimensionPixelOffset获得指定资源id对应的尺寸,第二个参数,应该是表示如果没有这个属性则返回指定的值
        //返回的是绝对尺寸单位,而非相对尺寸,即不是dp/sp。所以第二个参数就需要从dp转换成px,因此用到DensityUtil类
        //mScaleHeight=typeArray.getDimensionPixelOffset(R.styleable.ImageTextView_drawableHeight,DensityUtil.dip2px(context,20f))
        mScaleHeight=typeArray.getDimensionPixelOffset(R.styleable.ImageTextView_drawableHeight,20)
        println("mScaleHeight的值得是多少$mScaleHeight")
        mScaleWidth=typeArray.getDimensionPixelOffset(R.styleable.ImageTextView_drawableWidth,20)
        println("mScaleWidth的值得是多少$mScaleWidth")
        //第二个参数也是设置默认值
        mPosition=typeArray.getInt(R.styleable.ImageTextView_postion,3)

    }

    //重写TextView的方法onMeasure,这个方法一般用来设置自定义view的尺寸。如果不重写此方法,那么默认尺寸与父控件相同
    //
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        println("在设置尺寸中!!!!!!!")
        println(mDrawable?.bounds)
        //报错 smart cast to X is impossible,because X is a mutable property that could hava been changed by this time
        //if(mDrawable!=null){...}
        //mDrawable!!.setBounds(0,0,300,300)

        //DensityUtil.dip2px转换有问题
        //mDrawable!!.setBounds(0,0,DensityUtil.dip2px(context,mScaleWidth!!.toFloat()),DensityUtil.dip2px(context,mScaleHeight!!.toFloat()))

        var scale=context.resources.displayMetrics.density;
        var dpScaleHeight:Float=mScaleHeight!!*scale+0.5f
        var dpScaleWidth:Float=mScaleWidth!!*scale+0.5f
        mDrawable!!.setBounds(0,0,dpScaleWidth!!.toInt(),dpScaleHeight!!.toInt())



        println(mDrawable?.bounds)
        this.setCompoundDrawables(null,mDrawable,null,null)

    }

//    override fun onDraw(canvas: Canvas?) {
//        super.onDraw(canvas)
//        when(mPosition){
//            1->this.setCompoundDrawables(mDrawable,null,null,null)
//            2->this.setCompoundDrawables(null,mDrawable,null,null)
//            3->this.setCompoundDrawables(null,null,mDrawable,null)
//            4->this.setCompoundDrawables(null,null,null,mDrawable)
//        }
//    }

//    protected fun setDrawableLeft(drawable: Drawable){
//        this.mDrawable=drawable;
//    }
//
//    protected fun setDrawableLeft(drawableRes:Int,context: Context){
//        //
//        var mDrawable= ContextCompat.getDrawable(context,R.drawable.ic_php)
//        invalidate();
//    }
    
}

·在布局中使用ImageTextView

在根布局中添加:

xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"

(之后在ImageTextView中使用自定的属性要使用标签<app:> 而不是<android:>

添加布局代码,因为属于ViewPager的滑动页面,所以新建一个fragment_home_nav_program.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="wrap_content"
    android:orientation="horizontal">

    <com.vocus.justtest.view.ImageTextView
        android:id="@+id/nav_item_php"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="15dp"
        android:layout_marginRight="15dp"
        android:drawableTop="@drawable/ic_nav_php"
        android:gravity="center"
        android:text="Php"
        app:drawable="@drawable/ic_nav_php"
        app:drawableHeight="25dp"
        app:drawableWidth="25dp" />

    <com.vocus.justtest.view.ImageTextView
        android:id="@+id/nav_item_c"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="15dp"
        android:layout_marginRight="15dp"
        android:drawableTop="@drawable/ic_nav_c"
        android:gravity="center"
        android:text="C/c++"
        app:drawable="@drawable/ic_nav_c"
        app:drawableHeight="25dp"
        app:drawableWidth="25dp" />

    <com.vocus.justtest.view.ImageTextView
        android:id="@+id/nav_item_java"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="15dp"
        android:layout_marginRight="15dp"
        android:drawableTop="@drawable/ic_nav_java"
        android:gravity="center"
        android:text="Java"
        app:drawable="@drawable/ic_nav_java"
        app:drawableHeight="25dp"
        app:drawableWidth="25dp" />

    <com.vocus.justtest.view.ImageTextView
        android:id="@+id/nav_item_python"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="15dp"
        android:layout_marginRight="15dp"
        android:drawableTop="@drawable/ic_nav_python"
        android:gravity="center"
        android:text="Python"
        app:drawable="@drawable/ic_nav_python"
        app:drawableHeight="25dp"
        app:drawableWidth="25dp" />
</LinearLayout>

·设置导航ViewPager的Adapter,新建HomePageNavAdapter类,这里先随便写一下

class HomePageNavAdapter : PagerAdapter() {
    override fun instantiateItem(container: ViewGroup, position: Int): Any {
        //return super.instantiateItem(container, position)
        when (position) {
            0 -> {
                var navView = LayoutInflater.from(container.context)
                    .inflate(R.layout.fragment_home_nav_program, container, false)
                container.addView(navView)
                return navView
            }
            1 -> {
                var textView = TextView(container.context)
                textView.text = "第二页..."
                container.addView(textView)
                return textView
            }
            2 -> {
                var textView = TextView(container.context)
                textView.text = "第三页..."
                container.addView(textView)
                return textView
            }
            else ->{
                var textView=TextView(container.context)
                textView.text="超出范围了"
                return textView
            }
        }

    }

    override fun isViewFromObject(view: View, `object`: Any): Boolean {
        return view == `object`
    }

    override fun getCount(): Int {
        return 3
    }

    override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
        container.removeView(`object` as View)
    }

}

·实现一条导航线跟着Viewpager的页面移动而移动,这篇文章ViewPager 的顶部滑动线 提供了一个思路,就是在viewpager滚动监听中,改变移动线的leftMargin。

我在尝试使用文章提到的方法的时候,遇到了几个问题

·当使用homepage_nav_moveLine.layoutParams.leftMargin获取移动线的leftMargin时,提示

cannot inline bytecide built with JVM target 1.8 into bytecode that is being built with JVM target 1.6

需要在build.grandle中添加

compileOptions {    sourceCompatibility = 1.8    targetCompatibility = 1.8}kotlinOptions {    jvmTarget = "1.8"}

·当给它赋值的时候,提示

val cannot be reassigned

查看.leftMargin方法在View类中的定义

inline val View.marginLeft: Int    get() = (layoutParams as? MarginLayoutParams)?.leftMargin ?: 0

所以只要改成

homepage_nav_moveLine.layoutParams as ViewGroup.MarginLayoutParams).leftMargin

 另外,后面用到手机屏幕的尺寸信息,所以定义一个类ScreenDimen,并把它定义成单例模式。关于带参数的单例模式,参考

class ScreenDimen private constructor(context: Context){
    private var context:Context?=null
    private var windowManager:WindowManager?=null
    private var displayMetrics:DisplayMetrics?=null

    init{
        windowManager=(context.getSystemService(Context.WINDOW_SERVICE) as WindowManager)
        displayMetrics= DisplayMetrics()
        windowManager!!.defaultDisplay.getMetrics(displayMetrics)
    }

    companion object{
        var instance:ScreenDimen?=null

        fun getInstance(context: Context):ScreenDimen{
            if(instance==null){
                synchronized(ScreenDimen::class){
                    if(instance==null){
                        instance= ScreenDimen(context)
                    }
                }
            }
            return instance!!
        }
    }
    
    //获得屏幕宽度pixel
    fun getScreenWidthPix():Int{
        return displayMetrics!!.widthPixels
    }

    //获得宽度pixel
    fun getScreenHeightPix():Int{
        return displayMetrics!!.heightPixels
    }
    
    //获取屏幕宽度dp
    fun screenWidthDp():Int{
        var  screenWidthDp=getScreenWidthPix()/displayMetrics!!.density
        return screenWidthDp.toInt()
    }

    fun screenHeightDp():Int{
        var  screenHeightDp=getScreenHeightPix()/displayMetrics!!.density
        return screenHeightDp.toInt()
    }

}

· 最后写成这样了

 //加载导航ViewPager
        vp_homePageNav.adapter = HomePageNavAdapter()
        
        //define a move line
        vp_homePageNav.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
            private var currentPage: Int? = null
            private var screenWidth = ScreenDimen.getInstance(context!!).getScreenWidthPix()//屏幕像素
            private var moveLineDefaultLeftMargin = (homepage_nav_moveLine.layoutParams as ViewGroup.MarginLayoutParams).leftMargin

            override fun onPageScrollStateChanged(state: Int) {
            }

            override fun onPageScrolled(
                position: Int, //0,1,2
                positionOffset: Float,
                positionOffsetPixels: Int
            ) {
                //有延时
                var mvLeftMargin=0
                var progLeftMargin=(screenWidth/3-homepage_nav_moveLine.width)/2
                var docLetLeftMargin=screenWidth/3+(screenWidth/3-homepage_nav_moveLine.width)/2
                var sourceLeftMargin=(screenWidth/3)*2+(screenWidth/3-homepage_nav_moveLine.width)/2

                when (position) {
                    0 -> {
                        view!!.invalidate()
                        //homepage_nav_prog.typeface=Typeface.defaultFromStyle(Typeface.BOLD)
                        mvLeftMargin = progLeftMargin
                        mvLeftMargin += positionOffsetPixels / 3
                        (homepage_nav_moveLine.layoutParams as ViewGroup.MarginLayoutParams).leftMargin=mvLeftMargin
                    }
                    1 -> {
                        view!!.invalidate()
                        mvLeftMargin=docLetLeftMargin
                        mvLeftMargin += positionOffsetPixels / 3
                        (homepage_nav_moveLine.layoutParams as ViewGroup.MarginLayoutParams).leftMargin=mvLeftMargin
                    }
                    2 -> {
                        view!!.invalidate()
                        mvLeftMargin =sourceLeftMargin
                        mvLeftMargin += positionOffsetPixels / 3
                        (homepage_nav_moveLine.layoutParams as ViewGroup.MarginLayoutParams).leftMargin=mvLeftMargin
                    }
                }

            }
            override fun onPageSelected(position: Int) {
                currentPage = position //保存当前所在页面
            }

        })

        //add new code
        
    }

效果不理想,滑动页面,线的移动会延时,而且没有平滑移动

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!