I am trying to display different charts based on the user selection via a BottomNavigationView.
The UK looks like this:
The idea is that for each of the buttons clicked on the bottom a different chart will be shown.
The issue I have is that I cant get a different chart to show for each click.
So far I have tried using a single view and invalidating, adding a newly constructed chart and invalidating the view again.
statsNavigationBar.setOnNavigationItemSelectedListener {
when(it.itemId){
R.id.chartView1 -> {
chartView1.bringToFront()
chartView1.invalidate()
chartView1.setChart(createLineChart1())
chartView1.invalidate()
}
R.id.chartView2 -> {
chartView1.bringToFront()
chartView1.invalidate()
chartView1.setChart(createLineChart2())
chartView1.invalidate()
}
R.id.chartView3 -> {
}
}
true
}
This just shows the same chart no matter which button I click.
I have tried multiple AnyChartView objects:
statsNavigationBar.setOnNavigationItemSelectedListener {
when(it.itemId){
R.id.chartView1 -> {
chartView1.bringToFront()
chartView1.invalidate()
chartView1.setChart(createLineChart1())
chartView1.invalidate()
}
R.id.chartView2 -> {
chartView2.bringToFront()
chartView2.invalidate()
chartView2.setChart(createLineChart2())
chartView2.invalidate()
}
R.id.chartView3 -> {
}
}
true
}
This doesn't show any charts.
I have also tried using the same chart and resetting the data, which works, but I on the last button I want to have a BarChart so, resetting data won't work.
If anyone can suggest a way to show different charts when the user clicks an Item on the BottomNavigationView, I would be most grateful.
Here is my Layout file:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="match_parent"
tools:context=".StatsActivity">
<com.anychart.AnyChartView
android:id="#+id/chartView1"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/statsNavigationBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:background="#color/colorPrimaryDark"
/>
<com.anychart.AnyChartView
android:id="#+id/chartView2"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/statsNavigationBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:background="#color/colorAccent"
/>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/statsNavigationBar"
android:layout_width="0dp"
android:layout_height="54dp"
app:itemBackground="#color/colorAccent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="#menu/stats_navigation_items"
android:background="#color/colorAccent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Please, try to make the view active when the related chart is selected. Like this:
APIlib.getInstance().setActiveAnyChartView(anyChartView1);
This approach is used when multiple charts are required in a single layout.
Related
I'm trying to programmatically scroll to a particular item within my RecyclerView which is nested within a NestedScrollView.
The Problem
The NestedScrollView scrolls to the complete bottom rather than the desired item.
Note: It works correctly when desired item is the 2nd item, probably since that item is visible in the screen.
What I've tried
I've searched through a dozen solutions from StackOverFlow and came up with the function below.
I've tried:
binding.variantsRecyclerView.getChildAt()
binding.variantsRecyclerView.findViewWithTag()
binding.variantsRecyclerView.findViewHolderForAdapterPosition()
All these do return the correct item, (I know since the edit text within that item is focused as coded) however, the NestedScrollView does not scroll correctly to that item. It is almost always scrolling to the bottom. Sometimes however it scrolls to somewhere in between the required item instead of it's start. The only time this works is when the item is either the 1st or 2nd item. (As stated before)
private fun scrollToPosition(position: Int) {
if (position in 0 until variantsAdapter.itemCount) {
val view = binding.variantsRecyclerView.findViewWithTag<ConstraintLayout>("pos_$position")
if (view != null) {
val height = binding.nestedScrollView.height
val target = view.y.toInt()
binding.nestedScrollView.postDelayed({
binding.nestedScrollView.smoothScrollTo(0, target)
view.requestFocus()
}, 200)
}
}
}
My XML
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="match_parent"
android:background="#eff1f4">
<LinearLayout>...</LinearLayout>
<androidx.core.widget.NestedScrollView
android:id="#+id/nestedScrollView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:fillViewport="true"
app:layout_constrainedHeight="true"
app:layout_constraintVertical_bias="0"
app:layout_constraintBottom_toTopOf="#+id/btnNextLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/toolbarLayout">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/ui_10_dp"
android:layout_marginEnd="#dimen/ui_10_dp"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/variantsRecyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:nestedScrollingEnabled="false"
android:paddingTop="12dp"
android:paddingBottom="12dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:itemCount="2"
tools:listitem="#layout/sell_variant_row_item" />
<androidx.constraintlayout.widget.ConstraintLayout>
...
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<LinearLayout>...</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
My Understanding
After debugging, I found out that the NestedScrollView height is lesser than the y co-ordinate of the desired item. Hence it scrolls to the bottom of the view instead of the desired item. My understanding could be completely wrong and if so, please correct me.
I resolved this with a really simple fix.
private fun scrollToPosition(position: Int) {
if (position in 0 until variantsAdapter.itemCount) {
val view = binding.variantsRecyclerView.getChildAt(position)
if (view != null) {
val target = binding.variantsRecyclerView.top + view.top
binding.nestedScrollView.scrollY = target
}
}
}
All I wanted was to get the desired item within the RecyclerView to the top of the screen.
I have an activity that presents a list and some icons. My activity design contains a ConstraintLayout at the top level with the list and the icons as children.
Now there can be a situation where we don't have any data. In that case I would like the activity to show neither list nor icons but instead show an image and some error text.
How would I go about implementing this in a way that I can still edit the layout in the Android Studio design view? In other words, not just add the image and the error text overlapping my normal activity elements and toggle their visibility programmatically. Are there layers or something? Or switchable fragments?
Or like this: How can I group view elements in a way that I can show and hide the whole group in the designer?
Yes, in android are Fragments (Android docs) and You can create two fragments, one for a situation when data is loaded successfully and second if data isn't loaded. You can start with the first fragment and when You get information about failed loading data You can switch fragments to second with information about fail.
But I think You can do this in one ConstraintLayout layout. In Your main layout add on top image as You would like to display it and set visibility parameter android:visibility="gone". Now You see everything and can create UI in design mode. And when You get information about failed with loading data just change image parameter to visible.
To do it programmatically:
ImageView imageView = findViewById(R.id.imageDataFailed);
imageView.setVisibility(View.VISIBLE);
How it looks with two containers:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Here is container for success data load. When You failed load data set visibility="gone"-->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button Success"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- Here is container for failed data load. If You want to change design just set `visible`-->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button Failed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
I made a custom view with a progress bar. To add it in all my fragments i made a base fragment. In the method onActivityCreated i added the following code:
activity?.run {
loading = LoadingView(this)
loading?.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
loading?.visibility = View.GONE
(view as? ViewGroup)?.addView(loading)
}
And it works, but, when i make it visible, the buttons overlapped the progres bar in my LoadingView. So, when i show it i added brintToFront() method on a first test (that didnt work) and i saw i also had to use invalidate
protected fun showLoading() {
loading?.visibility = View.VISIBLE
loading?.bringToFront()
loading?.invalidate()
}
As this wasnt working either i started looking for a solution here and i found that the solution could be adding translationZ or elevation properties. So i tried to, but none is working.
The XML file of my view is:
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
What am i doing wrong?
I've tried this and settings translationZ to 6f or more brings my view in front of buttons.
It seems that it should be set programmatically.
protected fun showLoading() {
loading?.translationZ = 6f
loading?.visibility = View.VISIBLE
}
I have some screens in my app where TalkBack does not infer the correct reading order. According to the documentation, I can use android:accessibilityTraversalAfter and friends to alter the reading order. But it does not work for me for elements within a focusable ViewGroup that should be read together.
The entire layout looks like that:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="match_parent"
android:accessibilityTraversalBefore="#id/before"
android:focusable="true"
tools:context=".MainActivity">
<TextView
android:id="#+id/before"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:accessibilityTraversalAfter="#id/before"
android:text="Before"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<TextView
android:id="#+id/after"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:accessibilityTraversalBefore="#id/after"
android:text="After"
app:layout_constraintBottom_toTopOf="#+id/before"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
It renders After in the middle of the screen, Before at the bottom. I want TalkBack to treat the entire screen as a single contiguous element, hence I set android:focusable to true. By default, TalkBack reads: "After, before". But I want it to read "Before, after". Although I've added android:accessibilityTraversalBefore and android:accessibilityTraversalAfter, it still reads "After, before". That's the output of the Node Tree Debugging:
TreeDebug: (-2147455381)429.FrameLayout:(0, 0 - 1080, 1920):A
TreeDebug: (30189)429.TextView:(42, 101 - 397, 172):TEXT{My Application}:A:supportsTextLocation
TreeDebug: (31150)429.ViewGroup:(0, 210 - 1080, 1794):Fa:focusable:accessibilityFocused
TreeDebug: (33072)429.TextView:(499, 951 - 581, 1002):TEXT{After}:A:supportsTextLocation
TreeDebug: (32111)429.TextView:(485, 1743 - 595, 1794):TEXT{Before}:A:supportsTextLocation
What am I doing wrong?
Just for completeness: minSdkVersion is 26, targetSdkVersion is 29.
I've dug into the problem and found out following: accessibilityTraversalBefore and accessibilityTraversalAfter flags in deed take effect, but only for what they are intended for - they are intended for Accessibility Service app (e.g. Talkback). In other words if you remove focusable attribute from root layout you'll see that navigation is correct.
But those flags do not affect as to how AccessibilityNode is constructed for root ViewGroup. As can be seen in sources of ViewGroup#onInitializeAccessibilityNodeInfoInternal() the actual text construction logic does not regard how children construct their navigation using upper mentioned flags.
In order to solve the problem I've removed excessive flags from layout xml as such:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:id="#+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true"
tools:context=".MainActivity">
<TextView
android:id="#+id/before"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:importantForAccessibility="no"
android:text="Before"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/after"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:importantForAccessibility="no"
android:text="After"
app:layout_constraintBottom_toTopOf="#+id/before"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
And inside the host activity/fragment:
val root = findViewById<ViewGroup>(R.id.root)
ViewCompat.setAccessibilityDelegate(root, object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(
host: View?,
info: AccessibilityNodeInfoCompat?
) {
val stringBuilder = StringBuilder()
root.children.forEach { view ->
val label = if (view is TextView) view.text else ""
stringBuilder.append("$label, ")
}
info?.text = stringBuilder.toString()
super.onInitializeAccessibilityNodeInfo(host, info)
}
})
This will result in the desired outcome: Talkback will pronounce "Before, After".
Unfortunately, this is an error prone code, meaning that if you restructure the view hierarchy in a way, that order of children gets swapped then this node text construction logic will become broken. Nevertheless, I couldn't come up with a better solution and cannot see it's possible to instruct parent to regard child ordering flags (based on sources).
Problem:
It seems when I have a specific combination of views and setVisibility between Visible and Gone the RecyclerView does not update properly after initial load. I have a RelativeLayout->ConstraintLayout->Constraint Group(with visibility dynamically set). The view within the constraint group is not updating properly.
Example Use Case of Problem:
So the linked code below it will show a search view at the top. The initial state of empty shows the view properly(With the search icon showing). Then if you type "T" then the search icon will disappear(it shouldn't). So if you either delete the T or type the next letter "E" then it shows again. Also if you delete all search text and type T again it will show.
View Code:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:bind="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="vm"
type="com.example.davidcorrado.myapplication.PlayerVM" />
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.constraint.ConstraintLayout
android:id="#+id/playerCell_main_layout"
android:layout_width="match_parent"
android:layout_height="84dp"
android:layout_alignParentTop="true">
<android.support.constraint.Group
android:id="#+id/playerCell_status_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="#{vm.showingStatusIcons}"
app:constraint_referenced_ids="playerCell_lineup_status_image" />
<ImageView
android:id="#+id/playerCell_lineup_status_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:src="#drawable/ic_search" />
</android.support.constraint.ConstraintLayout>
</RelativeLayout>
</layout>
Quirks that might help:
1) If I move the visibility line to the relativeLayout or the ConstraintLayout the things seem to work
2) If I remove the RelativeLayout view things seem to work.
See the below for the full code example:
https://github.com/DavidCorrado/LayoutIssue
As far as I can tell, the reason the search icon disappears is due to the call to ObservableList.clear() then ObservableList.addAll(*)
So the disappear happens on the clear, then the reappear happens on the addAll.
As the callbacks in the adapter kick in on both calls, my hunch is it animates the view to disappear, then animates to show when the addAll is triggered.
I have verified this by not actioning a 'List.clear()' and instead in your textChange listener simply adding more views to the list.
I'm not sure how you would clear and add items to the list without animating the clear, perhaps there are some settings you can toggle in the RecyclerAdapter to ignore the remove of Views or not animate the entry of Views?
My adjusted code in your callback for class PlayersListVM
class PlayersListVM {
val filteredVms: ObservableList<ViewModel> = ObservableArrayList<ViewModel>()
val showing = PlayerVM(isShowing = true)
val missing = PlayerVM()
init {
filteredVms.add(showing)
}
fun onQueryChanged(newText: String) {
if (filteredVms.size % 2 == 1) filteredVms.add(missing)
else filteredVms.add(showing)
}
}