1+ package demo.plot.view
2+
3+ import android.app.Activity
4+ import android.graphics.Color
5+ import android.os.Bundle
6+ import android.view.ViewGroup
7+ import android.widget.*
8+ import org.jetbrains.letsPlot.android.canvas.CanvasView
9+ import org.jetbrains.letsPlot.core.util.sizing.SizingPolicy
10+ import org.jetbrains.letsPlot.intern.toSpec
11+ import org.jetbrains.letsPlot.raster.builder.MonolithicCanvas
12+ import org.jetbrains.letsPlot.raster.view.PlotCanvasFigure
13+ import org.jetbrains.letsPlot.themes.flavorDarcula
14+ import plotSpec.BarPlotSpec
15+
16+ class LayoutDemoActivity : Activity () {
17+ private companion object {
18+ private const val MAX_PLOT_WIDTH_DP = 390
19+ private const val MIN_PARENT_HEIGHT_DP = 50
20+ private const val MAX_PARENT_HEIGHT_DP = 390
21+ }
22+
23+ private lateinit var plotSizeLabel: TextView
24+ private lateinit var sizingPolicyOptions: RadioGroup
25+ private lateinit var preserveAspectRatio: CheckBox
26+ private lateinit var plotWidthSlider: SeekBar
27+ private lateinit var plotHeightSlider: SeekBar
28+
29+ private lateinit var containerOptionsGroup: ViewGroup
30+ private lateinit var fixedSizeOptionsGroup: ViewGroup
31+
32+ private lateinit var widthOptions: RadioGroup
33+ private lateinit var heightOptions: RadioGroup
34+ private lateinit var parentContainer: FrameLayout
35+ private lateinit var parentWidthSlider: SeekBar
36+ private lateinit var parentHeightSlider: SeekBar
37+
38+
39+ private lateinit var demoView: CanvasView
40+ private val plotFigure = PlotCanvasFigure ()
41+ private val plotSpec = (BarPlotSpec ().basic + flavorDarcula()).toSpec()
42+
43+ override fun onCreate (savedInstanceState : Bundle ? ) {
44+ super .onCreate(savedInstanceState)
45+ setContentView(R .layout.layout_demo_activity)
46+
47+ sizingPolicyOptions = findViewById(R .id.sizing_policy_options)
48+ preserveAspectRatio = findViewById(R .id.preserve_aspect_ratio)
49+ plotWidthSlider = findViewById(R .id.plot_width_slider)
50+ plotHeightSlider = findViewById(R .id.plot_height_slider)
51+ plotSizeLabel = findViewById(R .id.plot_size_label)
52+ containerOptionsGroup = findViewById(R .id.container_options_group)
53+ fixedSizeOptionsGroup = findViewById(R .id.fixed_size_options_group)
54+
55+ widthOptions = findViewById(R .id.width_options)
56+ heightOptions = findViewById(R .id.height_options)
57+ parentContainer = findViewById(R .id.parent_container)
58+ parentWidthSlider = findViewById(R .id.parent_width_slider)
59+ parentHeightSlider = findViewById(R .id.parent_height_slider)
60+
61+ demoView = CanvasView (this ).apply {
62+ figure = plotFigure
63+ setBackgroundColor(Color .GREEN )
64+ }
65+
66+ parentContainer.addView(demoView)
67+
68+ setupControls()
69+
70+ findViewById<RadioButton >(R .id.width_match).isChecked = true
71+ findViewById<RadioButton >(R .id.height_match).isChecked = true
72+ findViewById<RadioButton >(R .id.sizing_policy_container).isChecked = true
73+ updateDemoViewLayoutParams()
74+ updateParentContainerSize()
75+ updatePlotOptions()
76+ updateUiEnabledState()
77+ }
78+
79+ private fun setupControls () {
80+ sizingPolicyOptions.setOnCheckedChangeListener { _, checkedId ->
81+ updateUiEnabledState()
82+ updatePlotOptions()
83+ }
84+
85+ preserveAspectRatio.setOnCheckedChangeListener { _, checkedId ->
86+ updatePlotOptions()
87+ }
88+
89+ val plotSizeUpdateListener = object : SeekBar .OnSeekBarChangeListener {
90+ override fun onProgressChanged (seekBar : SeekBar ? , progress : Int , fromUser : Boolean ) {
91+ updatePlotOptions()
92+ }
93+
94+ override fun onStartTrackingTouch (seekBar : SeekBar ? ) {}
95+ override fun onStopTrackingTouch (seekBar : SeekBar ? ) {}
96+ }
97+ plotWidthSlider.setOnSeekBarChangeListener(plotSizeUpdateListener)
98+ plotHeightSlider.setOnSeekBarChangeListener(plotSizeUpdateListener)
99+
100+
101+ val layoutUpdateListener = RadioGroup .OnCheckedChangeListener { _, _ ->
102+ updateDemoViewLayoutParams()
103+ }
104+ widthOptions.setOnCheckedChangeListener(layoutUpdateListener)
105+ heightOptions.setOnCheckedChangeListener(layoutUpdateListener)
106+
107+ val parentSizeUpdateListener = object : SeekBar .OnSeekBarChangeListener {
108+ override fun onProgressChanged (seekBar : SeekBar ? , progress : Int , fromUser : Boolean ) {
109+ updateParentContainerSize()
110+ }
111+
112+ override fun onStartTrackingTouch (seekBar : SeekBar ? ) {}
113+ override fun onStopTrackingTouch (seekBar : SeekBar ? ) {}
114+ }
115+ parentWidthSlider.setOnSeekBarChangeListener(parentSizeUpdateListener)
116+ parentHeightSlider.setOnSeekBarChangeListener(parentSizeUpdateListener)
117+ }
118+
119+ private fun updatePlotOptions () {
120+ val plotWidth = MAX_PLOT_WIDTH_DP * plotWidthSlider.progress / 100.0
121+ val plotHeight = MAX_PLOT_WIDTH_DP * plotHeightSlider.progress / 100.0
122+
123+ plotSizeLabel.text = " Plot size: $plotWidth x $plotHeight "
124+
125+ val sizingPolicy = when (sizingPolicyOptions.checkedRadioButtonId) {
126+ R .id.sizing_policy_fixed -> SizingPolicy .fixed(width = plotWidth, height = plotHeight)
127+ R .id.sizing_policy_container -> SizingPolicy .fitContainerSize(preserveAspectRatio.isChecked)
128+ else -> error(" Unknown sizing policy option selected" )
129+ }
130+
131+ MonolithicCanvas .updatePlotFigureFromRawSpec(plotFigure, plotSpec, sizingPolicy) { _ -> }
132+ }
133+
134+ private fun updateDemoViewLayoutParams () {
135+ val width = getSelectedLayoutParam(widthOptions.checkedRadioButtonId)
136+ val height = getSelectedLayoutParam(heightOptions.checkedRadioButtonId)
137+
138+ demoView.layoutParams = FrameLayout .LayoutParams (width, height)
139+ }
140+
141+ private fun updateParentContainerSize () {
142+ val density = resources.displayMetrics.density
143+ val lp = parentContainer.layoutParams
144+
145+ val parentOfContainer = parentContainer.parent as ViewGroup
146+ val parentMaxWidth = parentOfContainer.width - parentOfContainer.paddingStart - parentOfContainer.paddingEnd
147+ val progressWidth = parentWidthSlider.progress / 100f
148+
149+ if (parentMaxWidth > 0 ) {
150+ lp.width = (parentMaxWidth * progressWidth).coerceAtLeast(100f ).toInt()
151+ }
152+
153+ val heightRangePx = (MAX_PARENT_HEIGHT_DP - MIN_PARENT_HEIGHT_DP ) * density
154+ val minHeightPx = (MIN_PARENT_HEIGHT_DP * density)
155+ val progressHeight = parentHeightSlider.progress / 100f
156+
157+ lp.height = (minHeightPx + (heightRangePx * progressHeight)).toInt()
158+
159+ findViewById<TextView >(R .id.parent_container_size_label).text = " Parent size: ${lp.width} x ${lp.height} "
160+
161+ parentContainer.layoutParams = lp
162+ }
163+
164+ private fun getSelectedLayoutParam (checkedId : Int ): Int {
165+ val density = resources.displayMetrics.density
166+ return when (checkedId) {
167+ R .id.width_match, R .id.height_match -> ViewGroup .LayoutParams .MATCH_PARENT
168+ R .id.width_wrap, R .id.height_wrap -> ViewGroup .LayoutParams .WRAP_CONTENT
169+ R .id.width_fixed, R .id.height_fixed -> (200 * density).toInt()
170+ else -> ViewGroup .LayoutParams .WRAP_CONTENT
171+ }
172+ }
173+
174+ private fun updateUiEnabledState () {
175+ when (sizingPolicyOptions.checkedRadioButtonId) {
176+ R .id.sizing_policy_container -> {
177+ // Enable the "fitContainerSize" options
178+ setGroupEnabled(containerOptionsGroup, true )
179+ // Disable the "fixed" size options
180+ setGroupEnabled(fixedSizeOptionsGroup, false )
181+ }
182+ R .id.sizing_policy_fixed -> {
183+ // Disable the "fitContainerSize" options
184+ setGroupEnabled(containerOptionsGroup, false )
185+ // Enable the "fixed" size options
186+ setGroupEnabled(fixedSizeOptionsGroup, true )
187+ }
188+ }
189+ }
190+
191+ private fun setGroupEnabled (viewGroup : ViewGroup , isEnabled : Boolean ) {
192+ viewGroup.isEnabled = isEnabled
193+ // Loop through all the children of the ViewGroup
194+ for (i in 0 until viewGroup.childCount) {
195+ val child = viewGroup.getChildAt(i)
196+ child.isEnabled = isEnabled
197+ // Set alpha to provide a visual cue for being disabled
198+ child.alpha = if (isEnabled) 1.0f else 0.5f
199+ }
200+ }
201+ }
0 commit comments