前言:
现在我们已经做好了点击饼图后该区域旋转至正下方,接下来的目标是将饼图点击后再在下方出现一个TextView显示详情.这就需要考虑到饼图不能再占据屏幕正中,而是要靠于上方,腾出一些空间,在下方显示详情文本,以防出现适配问题.
将饼图移到界面上方
这里可以采取两个方案来将实现该目的:
- 直接修改PieChart类或其父类的onMeasure方法(此方法只适用于从GitHub下载的源码添加到工程的方式)
- 创建一个类继承自PieChart类,重写onMeasure方法(通用)
这里作为一名强迫症患者,并且因为采用的是Gradle依赖添加方式,就自己创建了一个继承自PieChart的类,这里需要改动两个地方,一个在xml界面文件中,需要将com.github.mikephil.charting.charts.PieChart
换成自己包里的PieChart,另一个是PieFragment.kt中:
源码:
/*MPieChart.kt*/
import android.content.Context
import android.util.AttributeSet
import android.view.View
import com.github.mikephil.charting.charts.PieChart
import com.github.mikephil.charting.utils.Utils
/**
* Created by Administrator on 2018/2/12.
*/
class MPieChart : PieChart {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val size = Utils.convertDpToPixel(MeasureSpec.getSize(widthMeasureSpec).toFloat()).toInt()//这里size定死50f不合理,改一下
setMeasuredDimension(
Math.max(suggestedMinimumWidth,
View.resolveSize(size,
widthMeasureSpec)),
Math.max(suggestedMinimumHeight,
View.resolveSize(size,
widthMeasureSpec)))//这里将heightMeasureSpec改成widthMeasureSpec
}
}
这里涉及到了Kotlin的构造方法知识,该类没有主构造方法,使用了constructor关键字声明了三个从构造方法,修改过后的效果图如下:
在下方添加TextView
依赖:
/*百分比布局*/
compile"com.android.support:percent:27.0.2"
这里的版本号根据'com.android.support:appcompat-v7:27.0.2'
版本号设置
源码:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
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"
android:background="@android:color/black">
<android.support.v4.view.PagerTabStrip
android:id="@+id/chartTab"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</android.support.v4.view.ViewPager>
</android.support.constraint.ConstraintLayout>
这里增加了个android.support.v4.view.PagerTabStrip,只需在适配器里多重写一个getPageTitle
方法,设置标题
<!--fragment_pie_chart.xml-->
<?xml version="1.0" encoding="utf-8"?>
<android.support.percent.PercentRelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.example.administrator.spendingnote.MPieChart
android:id="@+id/pieChart"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
app:layout_widthPercent="80%" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_below="@id/pieChart"
android:layout_marginTop="10dp"
android:orientation="vertical"
app:layout_widthPercent="80%">
<View
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center_horizontal"
android:background="@drawable/triangle" />
<TextView
android:id="@+id/tv_des"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_des"
android:gravity="center" />
</LinearLayout>
</android.support.percent.PercentRelativeLayout>
这里对VIew
与TextView
的背景做了设置又一个三角图形的View
与圆角矩形的TextView
组合成聊天气泡图形:
<!--tv_des.xml-->
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="30dp"/>
<solid android:color="@android:color/white"/>
</shape>
<!--triangle.xml-->
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="45"
android:toDegrees="45"
android:pivotX="-40%"
android:pivotY="87">
<shape android:shape="rectangle" >
<solid android:color="@android:color/white"/>
</shape>
</rotate>
/*这里只改动了此方法,增添了详情显示*/
override fun onValueSelected(e: Entry?, h: Highlight?) {//当区域被选取的动作实现,这里的功能是区域被选取时,被选取区域旋转至正下方
val ratio = 360 / mChart.data.yValueSum//比例系数,每单位值所占度数
val index = (h?.x ?: 0f).toInt()
val angleByIndex = { i: Int -> mChart.data.dataSet.getEntryForIndex(i).value }//根据编号获得区域的值
var angle = 90f//初始旋转90度,加上默认的270度正好回原点
for (i in index downTo 0) {
angle -= angleByIndex(i) * ratio//旋转度数减去所选区域与前编号区域的度数
}
angle += angleByIndex(index) * ratio / 2//回退本区域一半度数,正好指向正下方
isRotationEnabled = true
rotationAngle = angle
isRotationEnabled = false
//以下为详情显示
val des="${data.dataSet.getEntryForIndex(index).label}花费${h?.y?:0}元,占比${String.format("%.0f", angleByIndex(index)/data.yValueSum*100)}%"
tvDes.text=des
}
这里遇到个问题,已经改正,总结一下:
由于是参考的别的教程,而自己对Fragment和ViewPager的认识不足,导致我实现了前面所有功能后在这里却遇到个很棘手的问题,TextView控件无法显示!
问题来自于多处的巧合,花了不少功夫才摸清问题,这是错误的代码:
<!--activity_main.xml-->
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
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"
android:background="@android:color/background_dark">
<android.support.v4.view.PagerTabStrip
android:id="@+id/chartTab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/background_light"/>
</android.support.v4.view.ViewPager>
</android.support.constraint.ConstraintLayout>
/*PieFragment.kt*/
private fun setData() {
val entries = ArrayList<PieEntry>()
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
tvDes.text=mData.date//新增,设置默认显示为当前账单时间
}
/*PieFragment.kt*/
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
mView = inflater.inflate(R.layout.fragment_pie_chart, container, false)
mChart = mView.find(R.id.pieChart)
tvDes=mView.find(R.id.tv_des)
initView()
return mChart
}
解析一下:
<include layout="@layout/fragment_pie_chart"/>
把fragment_pie_chart.xml引用到了activity_main.xml中,然后在Kotlin代码中又加载fragment_pie_chart.xml布局到Fragment
- 当
return mChart
时是将mChart控件覆盖了Fragment,填充在ViewPager中,所以看见的效果看起来是正常的,但是这样就只能显示一个控件,所以这必然是错误的用法. - 而我在没改动布局文件的情况下尝试使用
return mView
这个正确用法时会不显示加载的fragment_pie_chart.xml布局.
原因在于之前把此布局已经引用在了ViewPager中,这里又加载此布局,引起了这种晦涩的问题.,这个问题也印证了在当return mChart
之前不对mChart做解除父视图引用时会抛异常java.lang.IllegalStateException The specified child already has a parent. You must call removeView()
.