How to set snapping behaviour for TabLayout - android

I'm using TabLayout to show icon and name of different tabs. With TabLayout I'm using ViewPager to add Fragments per each tab. TabLayout with clickable tabs (and they are centered in a middle on click) is working, but I want to implement same functionality as RecyclerView SnapHelper can do. If I scroll TabLayout tabs it will snap view to the center of the screen and select it. This should show Fragment under TabLayout.
Is there any way how to do it?
This is my custom TabLayout which will center clicked view to the middle.
class CenteredTabLayout : TabLayout {
constructor(context: Context) : super(context) {}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
super.onLayout(changed, l, t, r, b)
val firstTab = (getChildAt(0) as ViewGroup).getChildAt(0)
val lastTab = (getChildAt(0) as ViewGroup).getChildAt((getChildAt(0) as ViewGroup).childCount - 1)
if (firstTab != null && lastTab != null){
ViewCompat.setPaddingRelative(getChildAt(0), width / 2 - firstTab.width / 2, 0, width / 2 - lastTab.width / 2, 0)
}
}
}

TabLayout selected tab gravity
tl;dr With the default TabLayout, you cannot modify the snapping effect. Because the method to calculate the distance of its moving calculateScrollXForTab is private and cannot be replaced.
I wanted to do some customized snapping with my TabLayout as well. Eventually I gave up, and created my own RecyclerView and adapter for the tabs. I did not need the indicator that is provided by the TabLayout (created my custom indicator). So all I had to do was to implement ViewPager.OnPageChangeListener using my adapter.
Also reading up on how other source code did the scrolling really helped me.
For example, https://github.com/nshmura/RecyclerTabLayout

Related

Using OnStateChanged listener simultaneously with OnTouchListener

I am implementing a rotary knob based on this library.
This View comes with a onStateChanged listener that tells me the current position of the knob.
val knob = findViewById<View>(R.id.knob) as Knob
knob.setOnStateChanged(object: Knob.OnStateChanged{
override fun onState(state: Int) {
// do stuff
}
})
In addition to that, I want to know when the user is no longer pressing/holding the knob (similar to a button release). I tried to achieve this with a onTouch listener.
knob.setOnTouchListener(object: View.OnTouchListener{
override fun onTouch(view: View?, event: MotionEvent?): Boolean {
// do stuff
}
})
Problem: When I add a second onTouch listener, it is no longer possible to hold and rotate the view for some reason. I do not know whether this is a problem of this particular library or Android in general.
Any suggestions on how to implement the wanted features?
A view can only have a single OnTouchListener. Unfortunately, the author of that library implemented some of its functionality using an OnTouchListener, which prevents users from using an OnTouchListener.
An alternative for you would be to subclass Knob and override onMotionEvent, like this:
class MyKnob: Knob {
constructor(context: Context): super(context)
constructor(context: Context, attrs: AttributeSet): super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int): super(context, attrs, defStyleAttr)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int): super(context, attrs, defStyleAttr, defStyleRes)
override fun onTouchEvent(event: MotionEvent): Boolean {
val result = super.onTouchEvent(event)
if (event.action == MotionEvent.ACTION_UP) {
// User released knob
}
return result
}
}
Then you'd need to use this class in your layout instead of the original Knob class.

Add contentDescription for custom tabList view

I have custom view of scrollable header with tabs (4 items). I want to add contentDesciption for that like it is with CheckBox, phone automatically recognizes when checkBox is selected and says "Selected" or "Unselected"
Is it possible to implement it on custom view? I tried to inherit from Checkable() and override setChecked(), isChecked() functions but it doesn't work.
class ProductTabs #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr)
{}
I tried to use AccessibilityDelegate
binding.tabLabels.setAccessibilityDelegate(object : AccessibilityDelegate() {
override fun onInitializeAccessibilityNodeInfo(
host: View,
info: AccessibilityNodeInfo
) {
// Let the default implementation populate the info.
super.onInitializeAccessibilityNodeInfo(host, info)
// Set some other information.
info.isEnabled = host.isEnabled
host.contentDescription = "selected"
}
override fun sendAccessibilityEvent(host: View?, eventType: Int) {
super.sendAccessibilityEvent(host, eventType)
host?.contentDescription = "selected"
}
})

Android navigation icon is not vertically aligned when using custom toolbar

I have used a custom toolbar class so i can align the title to the right and every thing works fine except the navigation back icon is not vertically aligned
and this is the custom toolbar class
class RTLToolbar #JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : Toolbar(context, attrs, defStyleAttr) {
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
super.onLayout(changed, l, t, r, b)
val childCount = childCount
for (i in 0 until childCount) {
val view = this.getChildAt(i)
if (view is TextView) {
forceTitleCenter(view,l, r)
break
}
}
}
private fun forceTitleCenter(view: TextView, l: Int, r: Int) {
val top = view.top
val bottom = view.bottom
view.layout(l, top, r, bottom)
navigationIcon?.let{ view.setPadding(it.intrinsicWidth,0,0,0) }
view.gravity = Gravity.RIGHT
}
}
i found this xml attribute
app:buttonGravity="center_vertical"
and it did the job, now the back icon is aligned with the title

How to center title in CollapsingToolbarLayout with back icon visible

As you can see in the picture. The title is slightly off centered because of the back button on the top-left.
How can I center the title?
I dig around the source code in CollapsingToolbarLayout and finally find a trick to center the title even when the back button is visible.
How to
1. Add this class to your code:
class CollapseLayoutCenteredTitleToolbar #JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = android.support.v7.appcompat.R.attr.toolbarStyle) : Toolbar(context, attrs, defStyleAttr) {
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
super.onLayout(changed, l, t, r, b)
(0 until childCount).forEach {
val view = getChildAt(it)
// 除了CollapsingToolbarLayout,没有谁会加一个纯的View进来
if (view.javaClass == View::class.java) {
view.left = 0
}
}
}
}
Change your layout to something like this:
<demo.CollapseLayoutCenteredTitleToolbar
android:id="#+id/main_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
/>
</android.support.design.widget.CollapsingToolbarLayout>
Enjoy

Kotlin Anko Custom View Parent scope

If we are building a custom View, for example, something like this:
class FrameLayoutNormal: FrameLayout{
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
textView{
lparams(...)
}
}
we can't define lparams, because the compiler doesn't know who the parent is. If we wrap the textView inside a FrameLayout it works, and you scan specify a layout parameter. But in a custom view, the parent is itself. So how can we make the children be aware of that so we can use the extension?
Is there any way to get it working, besides extending from: _FrameLayout ?`
An old question, but since it is common ...
Applying the answer from https://github.com/Kotlin/anko/issues/267
I think you might want something like this:
class FrameLayoutNormal: AnkoComponent<Context> {
override fun createView(ui: AnkoContext<Context>): View {
return with(ui) {
frameLayout {
textView("Hello") {
}.lparams()
}
}
}
}
inline fun ViewManager.frameLayoutNormal(theme: Int = 0) = frameLayoutNormal(theme) {}
inline fun ViewManager.frameLayoutNormal(theme: Int = 0, init: View.(frameLayoutNormal: FrameLayoutNormal) -> Unit): View {
val fln = FrameLayoutNormal()
return ankoView({ fln.createView(AnkoContext.create(it))}, theme, {init(fln)})
}
This allows the component to be used in the ANKO DSL. One downside to this approach is that the custom component is a View, not a ViewGroup, and thus can not have additional children added outside of its definition. It is challenging/laborious to make a custom component which is a ViewGroup that can be used in ANKO DSL (if I understand correctly).

Categories

Resources