第二步:将数据以饼状图显示
添加依赖:
这里我将使用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要高.