Android的Kotlin尝试之记账APP2

第二步:将数据以饼状图显示

添加依赖:

这里我将使用MPAndroidChart库,可以画各种各样的图表,这里是相关的wiki介绍
添加依赖:

allprojects {
    repositories {
        maven { url "https://jitpack.io" }
    }
}
dependencies {
    /*MPAndroidChart(画各式各样的图表)*/
    implementation 'com.github.PhilJay:MPAndroidChart:v3.0.3'
}

布局文件

<!--activity_main.xml-->
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/constraintLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v4.view.ViewPager
        android:id="@+id/vpMain"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

    </android.support.v4.view.ViewPager>
</android.support.constraint.ConstraintLayout>
<!--fragment_pie_chart-->
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.example.administrator.spendingnote.MPieChart
        android:id="@+id/pieChart"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</android.support.constraint.ConstraintLayout>

源码:

/*MainActivity.kt*/

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.v4.app.FragmentPagerAdapter
import android.support.v4.view.ViewPager
import com.github.salomonbrys.kotson.fromJson
import com.google.gson.Gson
import org.jetbrains.anko.*

class MainActivity : AppCompatActivity() {
    private val dataJson = """[{"date":"2017年五月","obj":[{"title":"外卖","value":30},{"title":"娱乐","value":30},{"title":"其他","value":40}]},{"date":"2017年五月","obj":[{"title":"外卖","value":15},{"title":"娱乐","value":40},{"title":"其他","value":135}]},{"date":"2017年五月","obj":[{"title":"外卖","value":50},{"title":"娱乐","value":25},{"title":"其他","value":25}]}]"""
    lateinit var mData: ArrayList<MonthBean>
    lateinit var vpMain:ViewPager
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        vpMain=find(R.id.vpMain)
        initData()
        initView()
    }

    private fun initData() {
        val gson = Gson()
        mData = gson.fromJson(dataJson)
    }

    private fun initView() {
        vpMain.adapter = object : FragmentPagerAdapter(supportFragmentManager) {
            override fun getCount(): Int {
                return mData.size
            }

            override fun getItem(position: Int): android.support.v4.app.Fragment {
                return newInstance(mData[position])
            }
        }
    }
}
/*PieFragment.kt*/

import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.github.mikephil.charting.charts.PieChart
import com.github.mikephil.charting.components.Description
import com.github.mikephil.charting.data.Entry
import com.github.mikephil.charting.data.PieData
import com.github.mikephil.charting.data.PieDataSet
import com.github.mikephil.charting.data.PieEntry
import org.jetbrains.anko.*


const val dataKey = "pie_fragment_data_key"

class PieFragment : Fragment() {
    private lateinit var mData: MonthBean
    private lateinit var mChart: PieChart
    private lateinit var mView:View
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //下面的getString改为getParcelable,所以需要MonthBan实现Parcelable接口
        mData = arguments?.getParcelable(dataKey) ?: MonthBean("isNull", ArrayList())
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        //通过inflater找到pieChart
        mView= inflater.inflate(R.layout.fragment_pie_chart, container, false)
        mChart = mView.find(R.id.pieChart)
        initView()
        return mView
    }

    private fun initView() {
        setData()
    }

    private fun setData() {
        val entries = ArrayList<PieEntry>()
        //这里使用mapTo方法代替for循环
        mData.obj.mapTo(entries) { PieEntry(it.value.toFloat(), it.title) }
        val mPieDataSet = PieDataSet(entries, "MoneySpending")
        //设置饼图的颜色
        mPieDataSet.colors = listOf(Color.rgb(216, 77, 219), Color.rgb(183, 56, 63), Color.rgb(247, 85, 47))
        val mPieData = PieData(mPieDataSet)
        mChart.data = mPieData
        //刷新mChart
        mChart.invalidate()
    }
}


fun newInstance(data: MonthBean): PieFragment {
    val args = Bundle()
    //这边不再传入String而是传入
    args.putParcelable(dataKey, data)
    val fragment = PieFragment()
    fragment.arguments = args
    return fragment
}

这里注意一下Fragment引用控件不能直接使用find而是需要用 inflater.inflate(R.layout.fragment_pie_chart, container, false).find(R.id.pieChart)加载布局,而这边往往大家是习惯传参为inflate(R.layout.activity_main,null),但是官方不推荐这样使用.


之前由于错误代码引发的问题,这里可忽视

这里有个坑,就是当程序运行后滑动Fragment会闪退抛异常:java.lang.IllegalStateException The specified child already has a parent. You must call removeView()出问题的是viewpager中的view。当切换不同的viewpager(即fragment,每个fragment中装载了一个viewpager)时,调用了startActivity()方法的时候,传入了相同的id,会返回相同的对象。而当我们在第二次调用的时候,传入了相同的id是复用了原来的view,这就导致了view被指定多个parent view。

所以解决办法就是,在使用这个view之前首先判断其是否存在parent view,这调用getParent()方法可以实现。如果存在parent view,那么就调用removeAllViewsInLayout()方法。代码如下:

with(mChart.parent as? ViewGroup){
     this?.removeAllViewsInLayout()
}

/*BeanData.kt*/

import android.os.Parcel
import android.os.Parcelable

data class MonthBean(val date: String, val obj: ArrayList<PieBean>) : Parcelable {
    constructor(source: Parcel) : this(
            source.readString(),
            ArrayList<PieBean>().apply { source.readList(this, PieBean::class.java.classLoader) }
    )

    override fun describeContents() = 0

    override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) {
        writeString(date)
        writeList(obj)
    }

    companion object {
        @JvmField
        val CREATOR: Parcelable.Creator<MonthBean> = object : Parcelable.Creator<MonthBean> {
            override fun createFromParcel(source: Parcel): MonthBean = MonthBean(source)
            override fun newArray(size: Int): Array<MonthBean?> = arrayOfNulls(size)
        }
    }
}


data class PieBean(val title: String, val value: Int) : Parcelable {
    constructor(source: Parcel) : this(
            source.readString(),
            source.readInt()
    )

    override fun describeContents() = 0

    override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) {
        writeString(title)
        writeInt(value)
    }

    companion object {
        @JvmField
        val CREATOR: Parcelable.Creator<PieBean> = object : Parcelable.Creator<PieBean> {
            override fun createFromParcel(source: Parcel): PieBean = PieBean(source)
            override fun newArray(size: Int): Array<PieBean?> = arrayOfNulls(size)
        }
    }
}

这里需要将MonthBean与PieBean实现Parcelable接口,Parcelable方式的实现原理是将一个完整的对象进行分解,
而分解后的每一部分都是Intent所支持的数据类型,这样也就实现传递对象的功能了。效率比Java自带Serializable要高.

效果图:

这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_37258787/article/details/79275603