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
}
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 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.
I have a Recyclerview that contains CardViews. The CardViews contain a ViewPager2 and a few other views (text, etc). I want to capture when a user clicks anywhere on the CardView (the parent) and run some logic. The problem: even though the ViewPager is a child of the CardView, it still interferes with the CardView's onClickListener and if the user clicks on the portion of the CardView where the ViewPager is drawn the CardView's listener is never triggered.
This is interesting fundamentally because I would expect the parent view to supersede any click listeners in the children, but the ViewPager seems to be a unique layout in this regard.
I've looked into solutions here but i'm not sure if any of them are the best practice since this onClick logic resides in a RecyclerView in my case. Since i'm instantiating the listener in my ViewHolder's init method it should be avoiding unnecessary object instantiation but i'm cautious not to make an inefficient solution.
The requirements for my solution are:
If a user clicks anywhere on my CardView, run my onClickListener (which'll launch a fragment)
If a user swipes the ViewPager within my CardView, let the ViewPager handle the event as usual and don't run the CardView's onClick logic
What's the proper way to do this? With my code below, clicks outside of the ViewPager are working but clicks on top of the ViewPager don't trigger my fragment-launching logic.
ViewHolder
class RaffleViewHolder(cardView: CardView) : RecyclerView.ViewHolder(cardView), View.OnClickListener {
val itemName = cardView.itemName
val viewPager = cardView.viewPager
val featuredWedge = cardView.quarter_circle
val imageViewPagerAdapter = RaffleItemViewPagerAdapter()
init {
viewPager.adapter = imageViewPagerAdapter
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
viewPager.setPageTransformer(ViewPagerDepthPageTransformer())
}
cardView.setOnClickListener(this)
}
override fun onClick(view: View?) {
Timber.d("onclick fired in recyclerviewadapter")
//do stuff. This is only reached for clicks outside of the ViewPager
}
}
XML for CardView
<androidx.cardview.widget.CardView
android:id="#+id/itemCard"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:elevation="2dp"
app:cardBackgroundColor="#color/white">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/viewPager"
android:layout_width="300dp"
android:layout_height="300dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/itemName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/viewPager" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
I am working on BottomSheetDialogFragment my requirement is to create Bottom menu,
Where if I click outside fragment area it should not cancel the Dialog and should persist.
ISSUE:
And Event outside the Fragment should propagate to the lower fragment view/fragment.
I have already tried below(doesn't work for BottomDialogFragment):
Allow outside touch for DialogFragment
To stop the dialog cancel i tried Below(i call setCancelable(boolean) in onStart() of BottomDialogFragment):
#Override
public void setCancelable(boolean cancelable) {
super.setCancelable(cancelable);
BottomSheetDialog dialog = (BottomSheetDialog) getDialog();
dialog.setCanceledOnTouchOutside(cancelable);
View bottomSheetView = dialog.getWindow().getDecorView().findViewById(R.id.design_bottom_sheet);
BottomSheetBehavior.from(bottomSheetView).setHideable(cancelable);
}
reference
EDIT: Found it the hard way there is no other go then using Coordinate layout.The best solution for BottomSheetDialog is here
This Solution solve's the issue but bring's in one more issue. i.e all the actionMode event's are not navigated while all other app event's are.
And this is my best solution to the problem
Try code below in your BottomSheetDialog:
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return (super.onCreateDialog(savedInstanceState) as BottomSheetDialog).apply {
setCanceledOnTouchOutside(false)
}
}
or wrap by <CoordinatorLayout> for instance your <ConstraintLayout> and implement <layout /> and attach to BottomSheetBehavior.
As been said by Pankaj Kumar, this is not possible by default. However, I found a workaround that works and allows touches to views outside of the bottom sheet while keeping the bottom sheet open
You can override the layout of the BottomSheetDialog like so:
values/refs.xml
<resources xmlns:tools="http://schemas.android.com/tools">
<item name="design_bottom_sheet_dialog" type="layout" tools:override="true">#layout/custom_design_bottom_sheet_dialog</item>
</resources>
layout/custom_design_bottom_sheet_dialog.xml
<?xml version="1.0" encoding="utf-8"?>
<!--
~ This is an override of the design_bottom_sheet_dialog from material library
-->
<FrameLayout
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/container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="#+id/coordinator"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<View
android:id="#+id/touch_outside"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="gone"
android:importantForAccessibility="no"
android:soundEffectsEnabled="false"
tools:ignore="UnusedAttribute"/>
<FrameLayout
android:id="#+id/design_bottom_sheet"
style="?attr/bottomSheetStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|top"
app:layout_behavior="#string/bottom_sheet_behavior"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</FrameLayout>
YourCustomBottomSheetDialogFragment
override fun onStart() {
super.onStart()
// Set layout for custom bottom sheet by allowing background touches
dialog?.window?.apply {
setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)
attributes = attributes.apply {
gravity = Gravity.BOTTOM
}
setDimAmount(0.0f)
}
}
By doing this, the dialog has a wrap_content height and the flags allow touches to be handled by views outside of this dialog
you should use android.support.design.widget.BottomSheetBehavior.
but if you want to have a bottomSheet in other class I suggest you use a Fragment and in this fragment open your bottomSheet
open your fragment in this way.
And in your fragment, open your bottomSheet in the following code:
in onInitViews
var mBottomSheetBehavior = BottomSheetBehavior.from(bottomSheetCoordinatorLayout)
mBottomSheetBehavior!!.state = BottomSheetBehavior.STATE_HIDDEN
mBottomSheetBehavior!!.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
override fun onStateChanged(bottomSheet: View, newState: Int) {
when (newState) {
BottomSheetBehavior.STATE_HIDDEN -> {
fragmentManager?.popBackStack()
}
//BottomSheetBehavior.STATE_COLLAPSED -> "Collapsed"
//BottomSheetBehavior.STATE_DRAGGING -> "Dragging..."
//BottomSheetBehavior.STATE_EXPANDED -> "Expanded"
//BottomSheetBehavior.STATE_SETTLING -> "Settling..."
}
}
override fun onSlide(bottomSheet: View, slideOffset: Float) {
//text_view_state!!.text = "Sliding..."
}
})
And your layout should be like this:
<android.support.design.widget.CoordinatorLayout 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"
android:layoutDirection="ltr">
<android.support.design.widget.CoordinatorLayout
android:id="#+id/bottomSheetCoordinatorLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:behavior_hideable="true"
app:behavior_peekHeight="55dp"
app:layout_behavior="#string/bottom_sheet_behavior">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/top_radius_primary_color"
android:paddingStart="#dimen/material_size_32"
android:paddingEnd="#dimen/material_size_32">
</RelativeLayout>
</android.support.design.widget.CoordinatorLayout>
</android.support.design.widget.CoordinatorLayout>
I hope it helps you
This is not possible till you are using BottomSheetDialogFragment. BottomSheetDialogFragment is a dialog and as behaviour of every dialog, it does not allow user interception on any view behind the dialog, although that is visible to user.
So to achieve this you need to use Fragment instead of BottomSheetDialogFragment. And yes it will require lot of code changes :) and you have to live without BottomSheetDialogFragment if you want to intercept views behind.
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)
}
}