mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-01-18 12:56:45 -06:00
[UI] Implement RecyclerTabLayout, enable swipe-to-refresh in timetable (#209)
* Enable swipe-to-refresh in timetable * Add basic RecyclerTabLayout view * Implement tab scrolling in RecyclerTabLayout * Implement tab clicking in RecyclerTabLayout * Add selected tab indicator to RecyclerTabLayout * Add ProGuard rules for RecyclerTabLayout * Set RecyclerTabLayout background
This commit is contained in:
parent
6371d71b7a
commit
f10bc42c7b
12
app/proguard-rules.pro
vendored
12
app/proguard-rules.pro
vendored
@ -55,6 +55,18 @@
|
|||||||
setIcons(android.widget.TextView);
|
setIcons(android.widget.TextView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# for RecyclerTabView
|
||||||
|
-keepclassmembernames class com.google.android.material.tabs.TabLayout {
|
||||||
|
tabIndicatorInterpolator;
|
||||||
|
}
|
||||||
|
-keepclassmembernames class com.google.android.material.tabs.TabLayout$TabView {
|
||||||
|
tab;
|
||||||
|
updateTab();
|
||||||
|
}
|
||||||
|
-keepclassmembernames class com.google.android.material.tabs.TabIndicatorInterpolator {
|
||||||
|
updateIndicatorForOffset(com.google.android.material.tabs.TabLayout, android.view.View, android.view.View, float, android.graphics.drawable.Drawable);
|
||||||
|
}
|
||||||
|
|
||||||
-keep class .R
|
-keep class .R
|
||||||
-keep class **.R$* {
|
-keep class **.R$* {
|
||||||
<fields>;
|
<fields>;
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2024-7-6.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:Suppress("UNCHECKED_CAST")
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ext
|
||||||
|
|
||||||
|
fun <T> Any.getDeclaredField(name: String): T? = this::class.java.getDeclaredField(name).run {
|
||||||
|
isAccessible = true
|
||||||
|
get(this@getDeclaredField) as? T
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Any.setDeclaredField(name: String, value: Any?) = this::class.java.getDeclaredField(name).run {
|
||||||
|
isAccessible = true
|
||||||
|
set(this@setDeclaredField, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Any.invokeDeclaredMethod(name: String, vararg args: Pair<Class<*>, Any?>): Any? =
|
||||||
|
this::class.java.getDeclaredMethod(name, *args.map { (k, _) -> k }.toTypedArray()).run {
|
||||||
|
isAccessible = true
|
||||||
|
invoke(this@invokeDeclaredMethod, *args.map { (_, v) -> v }.toTypedArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> Class<T>.invokeDeclaredConstructor(vararg args: Pair<Class<*>, Any>): T =
|
||||||
|
getDeclaredConstructor(*args.map { (k, _) -> k }.toTypedArray()).run {
|
||||||
|
isAccessible = true
|
||||||
|
newInstance(*args.map { (_, v) -> v }.toTypedArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> Class<*>.getDeclaredClass(name: String): Class<T> =
|
||||||
|
declaredClasses.first { it.simpleName == name } as Class<T>
|
@ -43,7 +43,7 @@ class AttendanceSummaryFragment : BaseFragment<AttendanceSummaryFragmentBinding,
|
|||||||
private var periodSelection = 0
|
private var periodSelection = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getRefreshScrollingView() = b.list
|
override fun getRefreshScrollingView() = b.scrollView
|
||||||
|
|
||||||
private val manager
|
private val manager
|
||||||
get() = app.attendanceManager
|
get() = app.attendanceManager
|
||||||
@ -149,7 +149,6 @@ class AttendanceSummaryFragment : BaseFragment<AttendanceSummaryFragmentBinding,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("SuspendFunctionOnCoroutineScope")
|
|
||||||
private fun processAttendance(): MutableList<Any> {
|
private fun processAttendance(): MutableList<Any> {
|
||||||
val attendance = when (periodSelection) {
|
val attendance = when (periodSelection) {
|
||||||
0 -> attendance
|
0 -> attendance
|
||||||
|
@ -42,7 +42,7 @@ internal fun BaseFragment<*, *>.setupCanRefresh() {
|
|||||||
// keep track of the touch state
|
// keep track of the touch state
|
||||||
when (event.action) {
|
when (event.action) {
|
||||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> isTouched = false
|
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> isTouched = false
|
||||||
MotionEvent.ACTION_DOWN -> isTouched = true
|
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> isTouched = true
|
||||||
}
|
}
|
||||||
// disable refresh when scrolled down
|
// disable refresh when scrolled down
|
||||||
if (view.canScrollVertically(-1))
|
if (view.canScrollVertically(-1))
|
||||||
|
@ -16,6 +16,8 @@ import com.google.android.material.tabs.TabLayout
|
|||||||
import com.google.android.material.tabs.TabLayoutMediator
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import pl.szczodrzynski.edziennik.ext.set
|
import pl.szczodrzynski.edziennik.ext.set
|
||||||
|
import pl.szczodrzynski.edziennik.ext.setDeclaredField
|
||||||
|
import pl.szczodrzynski.edziennik.ui.base.views.RecyclerTabLayout
|
||||||
|
|
||||||
abstract class PagerFragment<B : ViewBinding, A : AppCompatActivity>(
|
abstract class PagerFragment<B : ViewBinding, A : AppCompatActivity>(
|
||||||
inflater: ((inflater: LayoutInflater, parent: ViewGroup?, attachToParent: Boolean) -> B)?,
|
inflater: ((inflater: LayoutInflater, parent: ViewGroup?, attachToParent: Boolean) -> B)?,
|
||||||
@ -76,9 +78,15 @@ abstract class PagerFragment<B : ViewBinding, A : AppCompatActivity>(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
TabLayoutMediator(getTabLayout(), getViewPager()) { tab, position ->
|
when (val tabLayout = getTabLayout()) {
|
||||||
tab.text = getPageTitle(position)
|
is TabLayout -> TabLayoutMediator(tabLayout, getViewPager()) { tab, position ->
|
||||||
}.attach()
|
tab.text = getPageTitle(position)
|
||||||
|
}.attach()
|
||||||
|
|
||||||
|
is RecyclerTabLayout -> tabLayout.setupWithViewPager(getViewPager()) { tab, position ->
|
||||||
|
tab.setDeclaredField("text", getPageTitle(position))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onPageSelected(savedPageSelection)
|
onPageSelected(savedPageSelection)
|
||||||
}
|
}
|
||||||
@ -103,9 +111,9 @@ abstract class PagerFragment<B : ViewBinding, A : AppCompatActivity>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called to retrieve the [TabLayout] view of the pager fragment.
|
* Called to retrieve the [TabLayout] or [RecyclerTabLayout] view of the pager fragment.
|
||||||
*/
|
*/
|
||||||
abstract fun getTabLayout(): TabLayout
|
abstract fun getTabLayout(): ViewGroup
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called to retrieve the [ViewPager2] view of the pager fragment.
|
* Called to retrieve the [ViewPager2] view of the pager fragment.
|
||||||
|
@ -0,0 +1,198 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2024-7-7.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.base.views
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.graphics.drawable.updateBounds
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
|
import com.google.android.material.tabs.TabLayout
|
||||||
|
import com.google.android.material.tabs.TabLayoutMediator.TabConfigurationStrategy
|
||||||
|
import pl.szczodrzynski.edziennik.ext.getDeclaredField
|
||||||
|
import pl.szczodrzynski.edziennik.ext.invokeDeclaredMethod
|
||||||
|
import pl.szczodrzynski.edziennik.ext.onClick
|
||||||
|
import pl.szczodrzynski.edziennik.ext.setDeclaredField
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
class RecyclerTabLayout @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0,
|
||||||
|
) : RecyclerView(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
|
private val tabLayout: TabLayout = TabLayout(context, attrs)
|
||||||
|
private lateinit var linearLayoutManager: LinearLayoutManager
|
||||||
|
private lateinit var viewPager: ViewPager2
|
||||||
|
private lateinit var viewPagerAdapter: RecyclerView.Adapter<ViewHolder>
|
||||||
|
private lateinit var tabAdapter: Adapter
|
||||||
|
private lateinit var tabConfigurationStrategy: TabConfigurationStrategy
|
||||||
|
private lateinit var tab: TabLayout.Tab
|
||||||
|
private lateinit var tabSelectedIndicator: Drawable
|
||||||
|
private var tabIndicatorGravity: Int = 0
|
||||||
|
private var tabIndicatorInterpolator: Any? = null
|
||||||
|
|
||||||
|
private var selectedTab = 0
|
||||||
|
|
||||||
|
fun setupWithViewPager(pager: ViewPager2, strategy: TabConfigurationStrategy) {
|
||||||
|
linearLayoutManager = LinearLayoutManager(context).also {
|
||||||
|
it.orientation = HORIZONTAL
|
||||||
|
}
|
||||||
|
viewPager = pager
|
||||||
|
viewPagerAdapter = pager.adapter!!
|
||||||
|
tabAdapter = Adapter()
|
||||||
|
tabConfigurationStrategy = strategy
|
||||||
|
tab = TabLayout.Tab()
|
||||||
|
tabSelectedIndicator = tabLayout.tabSelectedIndicator
|
||||||
|
tabIndicatorGravity = tabLayout.tabIndicatorGravity
|
||||||
|
tabIndicatorInterpolator = tabLayout.getDeclaredField("tabIndicatorInterpolator")
|
||||||
|
|
||||||
|
this.addOnScrollListener(OnScrollListener())
|
||||||
|
viewPager.registerOnPageChangeCallback(OnPageChangeCallback())
|
||||||
|
viewPagerAdapter.registerAdapterDataObserver(AdapterDataObserver())
|
||||||
|
|
||||||
|
setWillNotDraw(false)
|
||||||
|
setAdapter(tabAdapter)
|
||||||
|
setHasFixedSize(true)
|
||||||
|
layoutManager = linearLayoutManager
|
||||||
|
itemAnimator = null
|
||||||
|
background = tabLayout.background
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas) {
|
||||||
|
super.onDraw(canvas)
|
||||||
|
// draw the selected tab indicator
|
||||||
|
var indicatorHeight = tabSelectedIndicator.bounds.height()
|
||||||
|
if (indicatorHeight < 0)
|
||||||
|
indicatorHeight = tabSelectedIndicator.intrinsicHeight
|
||||||
|
|
||||||
|
var indicatorTop = 0
|
||||||
|
var indicatorBottom = 0
|
||||||
|
when (tabLayout.tabIndicatorGravity) {
|
||||||
|
TabLayout.INDICATOR_GRAVITY_BOTTOM -> {
|
||||||
|
indicatorTop = height - indicatorHeight
|
||||||
|
indicatorBottom = height
|
||||||
|
}
|
||||||
|
|
||||||
|
TabLayout.INDICATOR_GRAVITY_CENTER -> {
|
||||||
|
indicatorTop = (height - indicatorHeight) / 2
|
||||||
|
indicatorBottom = (height + indicatorHeight) / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
TabLayout.INDICATOR_GRAVITY_TOP -> {
|
||||||
|
indicatorTop = 0
|
||||||
|
indicatorBottom = indicatorHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
TabLayout.INDICATOR_GRAVITY_STRETCH -> {
|
||||||
|
indicatorTop = 0
|
||||||
|
indicatorBottom = height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tabSelectedIndicator.updateBounds(top = indicatorTop, bottom = indicatorBottom)
|
||||||
|
tabSelectedIndicator.draw(canvas)
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class Adapter : RecyclerView.Adapter<Adapter.TabViewHolder>() {
|
||||||
|
override fun getItemCount() =
|
||||||
|
viewPagerAdapter.itemCount
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||||
|
TabViewHolder(tabLayout.TabView(context)).also { holder ->
|
||||||
|
holder.tabView.onClick {
|
||||||
|
viewPager.setCurrentItem(holder.lastPosition, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: TabViewHolder, position: Int) {
|
||||||
|
val tabView = holder.tabView
|
||||||
|
tabConfigurationStrategy.onConfigureTab(tab, position)
|
||||||
|
tabView.setDeclaredField("tab", tab)
|
||||||
|
tabView.invokeDeclaredMethod("updateTab")
|
||||||
|
tabView.setDeclaredField("tab", null)
|
||||||
|
tabView.isSelected = position == selectedTab
|
||||||
|
tabView.isActivated = position == selectedTab
|
||||||
|
holder.lastPosition = position
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class TabViewHolder(
|
||||||
|
val tabView: TabLayout.TabView,
|
||||||
|
var lastPosition: Int = 0,
|
||||||
|
) : ViewHolder(tabView)
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class OnScrollListener : RecyclerView.OnScrollListener() {
|
||||||
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||||
|
val left = tabSelectedIndicator.bounds.left
|
||||||
|
val right = tabSelectedIndicator.bounds.right
|
||||||
|
tabSelectedIndicator.updateBounds(left = left - dx, right = right - dx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class OnPageChangeCallback : ViewPager2.OnPageChangeCallback() {
|
||||||
|
override fun onPageScrolled(
|
||||||
|
position: Int,
|
||||||
|
positionOffset: Float,
|
||||||
|
positionOffsetPixels: Int,
|
||||||
|
) {
|
||||||
|
stopScroll()
|
||||||
|
|
||||||
|
val thisTab = findViewHolderForLayoutPosition(position)?.itemView
|
||||||
|
val nextTab = findViewHolderForLayoutPosition(position + 1)?.itemView
|
||||||
|
|
||||||
|
// scroll to the currently selected tab
|
||||||
|
val thisTabWidth = thisTab?.width ?: 0
|
||||||
|
val nextTabWidth = nextTab?.width ?: 0
|
||||||
|
val tabDistance = (thisTabWidth / 2) + (nextTabWidth / 2)
|
||||||
|
val offset = (width / 2.0f) - (thisTabWidth / 2.0f) - (tabDistance * positionOffset)
|
||||||
|
// 'offset' is the screen position of the tab's left edge
|
||||||
|
linearLayoutManager.scrollToPositionWithOffset(position, offset.toInt())
|
||||||
|
|
||||||
|
// update selection state of the current tab
|
||||||
|
val roundedPosition = Math.round(position + positionOffset)
|
||||||
|
if (selectedTab != roundedPosition) {
|
||||||
|
val previousTab = selectedTab
|
||||||
|
selectedTab = roundedPosition
|
||||||
|
tabAdapter.notifyItemRangeChanged(min(previousTab, selectedTab), 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the width and position of the selected tab indicator
|
||||||
|
tabIndicatorInterpolator?.invokeDeclaredMethod(
|
||||||
|
name = "updateIndicatorForOffset",
|
||||||
|
/* tabLayout = */ TabLayout::class.java to tabLayout,
|
||||||
|
/* startTitle = */ View::class.java to thisTab,
|
||||||
|
/* endTitle = */ View::class.java to nextTab,
|
||||||
|
/* offset = */ Float::class.java to positionOffset,
|
||||||
|
/* indicator = */ Drawable::class.java to tabSelectedIndicator,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class AdapterDataObserver : RecyclerView.AdapterDataObserver() {
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
override fun onChanged() =
|
||||||
|
tabAdapter.notifyDataSetChanged()
|
||||||
|
|
||||||
|
override fun onItemRangeChanged(positionStart: Int, itemCount: Int) =
|
||||||
|
tabAdapter.notifyItemRangeChanged(positionStart, itemCount)
|
||||||
|
|
||||||
|
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) =
|
||||||
|
tabAdapter.notifyItemRangeInserted(positionStart, itemCount)
|
||||||
|
|
||||||
|
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) =
|
||||||
|
tabAdapter.notifyItemRangeRemoved(positionStart, itemCount)
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) =
|
||||||
|
tabAdapter.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
@ -101,6 +101,8 @@ class TimetableDayFragment : BaseFragment<TimetableDayFragmentBinding, MainActiv
|
|||||||
}
|
}
|
||||||
private val dayView by dayViewDelegate
|
private val dayView by dayViewDelegate
|
||||||
|
|
||||||
|
override fun getRefreshScrollingView() = b.scrollView
|
||||||
|
|
||||||
override suspend fun onViewReady(savedInstanceState: Bundle?) {
|
override suspend fun onViewReady(savedInstanceState: Bundle?) {
|
||||||
this.inflater = AsyncLayoutInflater(requireContext())
|
this.inflater = AsyncLayoutInflater(requireContext())
|
||||||
|
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Kuba Szczodrzyński 2019-11-16.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package pl.szczodrzynski.edziennik.utils
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.util.AttributeSet
|
|
||||||
import android.view.MotionEvent
|
|
||||||
import android.widget.ScrollView
|
|
||||||
|
|
||||||
class ListenerScrollView(
|
|
||||||
context: Context,
|
|
||||||
attrs: AttributeSet? = null
|
|
||||||
) : ScrollView(context, attrs) {
|
|
||||||
|
|
||||||
private var onScrollChangedListener: ((v: ListenerScrollView, scrollX: Int, scrollY: Int, oldScrollX: Int, oldScrollY: Int) -> Unit)? = null
|
|
||||||
private var onRefreshLayoutEnabledListener: ((enabled: Boolean) -> Unit)? = null
|
|
||||||
private var refreshLayoutEnabled = true
|
|
||||||
|
|
||||||
init {
|
|
||||||
setOnTouchListener { _, event ->
|
|
||||||
if (event.action == MotionEvent.ACTION_UP) {
|
|
||||||
refreshLayoutEnabled = scrollY < 10
|
|
||||||
onRefreshLayoutEnabledListener?.invoke(refreshLayoutEnabled)
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onScrollChanged(l: Int, t: Int, oldl: Int, oldt: Int) {
|
|
||||||
onScrollChangedListener?.invoke(this, l, t, oldl, oldt)
|
|
||||||
if (t > 10 && refreshLayoutEnabled) {
|
|
||||||
refreshLayoutEnabled = false
|
|
||||||
onRefreshLayoutEnabledListener?.invoke(refreshLayoutEnabled)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setOnScrollChangedListener(l: ((v: ListenerScrollView, scrollX: Int, scrollY: Int, oldScrollX: Int, oldScrollY: Int) -> Unit)?) {
|
|
||||||
onScrollChangedListener = l
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setOnRefreshLayoutEnabledListener(l: ((enabled: Boolean) -> Unit)?) {
|
|
||||||
onRefreshLayoutEnabledListener = l
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,32 +2,27 @@
|
|||||||
~ Copyright (c) Kuba Szczodrzyński 2024-7-3.
|
~ Copyright (c) Kuba Szczodrzyński 2024-7-3.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<LinearLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<com.google.android.material.tabs.TabLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/tabLayout"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:background="?colorSurfaceContainerLow"
|
|
||||||
app:tabIndicatorColor="?colorPrimary"
|
<com.google.android.material.tabs.TabLayout
|
||||||
app:tabMaxWidth="300dp"
|
android:id="@+id/tabLayout"
|
||||||
app:tabMinWidth="90dp"
|
android:layout_width="match_parent"
|
||||||
app:tabMode="auto"
|
android:layout_height="wrap_content"
|
||||||
app:tabPaddingBottom="12dp"
|
app:tabMode="auto" />
|
||||||
app:tabPaddingEnd="16dp"
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
app:tabPaddingStart="16dp"
|
|
||||||
app:tabPaddingTop="12dp"
|
|
||||||
app:tabSelectedTextColor="?colorPrimary"
|
|
||||||
app:tabTextColor="?android:textColorPrimary" />
|
|
||||||
|
|
||||||
<androidx.viewpager2.widget.ViewPager2
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
android:id="@+id/viewPager"
|
android:id="@+id/viewPager"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
</LinearLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
@ -10,28 +10,22 @@
|
|||||||
android:id="@+id/timetableLayout"
|
android:id="@+id/timetableLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
tools:visibility="gone">
|
tools:visibility="gone">
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:background="?colorSurface">
|
|
||||||
|
|
||||||
<com.google.android.material.tabs.TabLayout
|
<pl.szczodrzynski.edziennik.ui.base.views.RecyclerTabLayout
|
||||||
android:id="@+id/tabLayout"
|
android:id="@+id/tabLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?colorSurfaceContainerLow"
|
|
||||||
app:tabIndicatorColor="?colorPrimary"
|
|
||||||
app:tabMaxWidth="300dp"
|
|
||||||
app:tabMinWidth="90dp"
|
|
||||||
app:tabMode="auto"
|
app:tabMode="auto"
|
||||||
app:tabPaddingBottom="12dp"
|
app:tabPaddingBottom="16dp"
|
||||||
app:tabPaddingEnd="16dp"
|
app:tabPaddingEnd="16dp"
|
||||||
app:tabPaddingStart="16dp"
|
app:tabPaddingStart="16dp"
|
||||||
app:tabPaddingTop="12dp"
|
app:tabPaddingTop="16dp" />
|
||||||
app:tabSelectedTextColor="?colorPrimary"
|
|
||||||
app:tabTextColor="?colorOnBackground" />
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<androidx.viewpager2.widget.ViewPager2
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
|
@ -8,18 +8,17 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<pl.szczodrzynski.edziennik.utils.ListenerScrollView
|
<androidx.core.widget.NestedScrollView
|
||||||
android:id="@+id/scrollView"
|
android:id="@+id/scrollView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible">
|
tools:visibility="visible">
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/dayFrame"
|
android:id="@+id/dayFrame"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
tools:layout_height="match_parent">
|
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/timeIndicatorMarker"
|
android:id="@+id/timeIndicatorMarker"
|
||||||
@ -37,7 +36,7 @@
|
|||||||
android:background="?colorTertiary"
|
android:background="?colorTertiary"
|
||||||
tools:layout_marginTop="100dp" />
|
tools:layout_marginTop="100dp" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
</pl.szczodrzynski.edziennik.utils.ListenerScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/progressBar"
|
android:id="@+id/progressBar"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user