I am trying to put a checkable overflow menu into my toolbar. The menu renders with the check boxes, but the text does not appear. Android studio renders the menu as intended but it does not on my AVD. I have tried changing the text color of the menu and using an action layout.
[![Intended render of menu][1]][1]
[![Actual render of menu][2]][2]
menu_main.xml
<?xml version="1.0" encoding="utf-8"?>
<menu 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"
tools:context=".MainActivity"
android:layout_height="wrap_content"
android:layout_width="wrap_content">
<item
android:id="#+id/tab_1"
android:checkable="true"
android:title="#string/tab_1"
app:showAsAction="never|withText"
android:actionLayout="#layout/action_layout"/>
<item
android:id="#+id/tab_2"
android:checkable="true"
android:title="#string/tab_2"
android:visible="true"
app:showAsAction="never|withText" />
<item
android:id="#+id/tab_3"
android:checkable="true"
android:title="#string/tab_3"
android:visible="true"
app:showAsAction="never|withText" />
</menu>
activity_main.kt
lateinit var sectionsPagerAdapter: SectionsPagerAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
sectionsPagerAdapter = SectionsPagerAdapter(this, supportFragmentManager)
val viewPager: ViewPager = findViewById(R.id.view_pager)
viewPager.adapter = sectionsPagerAdapter
val tabs: TabLayout = findViewById(R.id.tabs)
tabs.setupWithViewPager(viewPager)
val fab: FloatingActionButton = findViewById(R.id.fab)
val toolBar: Toolbar = findViewById(R.id.tool_bar)
setSupportActionBar(toolBar)
toolBar.showOverflowMenu()
var position: Int = 0
class onTabSelectListen : TabLayout.OnTabSelectedListener{
override fun onTabReselected(tab: TabLayout.Tab?) {
if (tab != null) {
position = tab.position
};
}
override fun onTabUnselected(tab: TabLayout.Tab?) {
Log.d("","")
}
override fun onTabSelected(tab: TabLayout.Tab){
position = tab.position;
}
}
tabs.addOnTabSelectedListener(onTabSelectListen())
fab.setOnClickListener { view ->
Snackbar.make(view, "Reloading...", Snackbar.LENGTH_SHORT)
.setAction("Action", null).show()
fab.animate()
.setDuration(500)
.rotationBy(-360f)
reload()
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.menu_main,menu)
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val itemId = item.itemId
if (item.isChecked) {
//TODO("UNCHECKD LOGIC")
item.isChecked = false //Toggles checkbox state.
} else {
//TODO("CHECKED LOGIC")
item.isChecked = true
}
return super.onOptionsItemSelected(item)
}
fun reload(){
(sectionsPagerAdapter.getItem(sectionsPagerAdapter.GOOGLE_POSITION) as WebFragment).reloadWebView()
(sectionsPagerAdapter.getItem(sectionsPagerAdapter.TWITTER_POSITION) as WebFragment).reloadWebView()
}
}```
[1]: https://i.stack.imgur.com/sEOFd.png
[2]: https://i.stack.imgur.com/Jqjwb.png
Try setting following property in Item:
android:showAsAction="ifRoom"
Instead of:
app:showAsAction="never|withText"
Adding this as an attribute under <toolbar> worked for me
app:popupTheme="#style/Theme.Assignment1.PopupOverlay"
Related
Hey I am working on search bar in android. I get the lead from this post. Now I want to try something more. Above post explanation in short :- I have searchview in the middle of screen. When we focus to on searchview we animate to go to top of screen and after remove focus goes to original position of search view. Now I want to show back arrow with initial screen load, look like this
Image 1
When we focus I need to show screen like this
Image 2
I tried some piece of code, but I am not succeed
ExploreConsultationsLayoutBinding.xml
<androidx.coordinatorlayout.widget.CoordinatorLayout
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:focusable="true">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appBar"
android:layout_width="match_parent"
android:gravity="bottom"
android:backgroundTint="#color/red_primary_80"
android:layout_height="?attr/collapsingToolbarLayoutLargeSize"
android:fitsSystemWindows="true">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#color/black">
<androidx.appcompat.widget.SearchView
android:id="#+id/searchView"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_marginStart="16dp"
app:iconifiedByDefault="false"
android:layout_marginEnd="16dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
/>
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
<!-- Scrollable content -->
</androidx.coordinatorlayout.widget.CoordinatorLayout>
ExploreConsultationsActivity.kt
package com.example.app.consultation
import android.content.Context
import android.graphics.Rect
import android.os.Bundle
import android.view.MotionEvent
import android.view.View
import android.view.inputmethod.InputMethodManager
import androidx.appcompat.widget.SearchView
import com.example.app.common.BaseActivity
import com.example.app.databinding.ExploreConsultationsLayoutBinding
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
class ExploreConsultationsActivity : BaseActivity() {
companion object {
const val CONSULTATION_LIST_KEY = "consultation_list"
}
private val binding by lazy { ExploreConsultationsLayoutBinding.inflate(layoutInflater) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
setupView()
}
fun setupView() {
hideActionBar()
setupSearchView()
}
fun hideActionBar() {
supportActionBar?.let { actionBar ->
actionBar.hide()
}
}
fun setupSearchView() {
binding.consultationSearchView.apply {
setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?) = false
override fun onQueryTextChange(newText: String?): Boolean {
if (newText != null) {
viewModel.queryText = newText
}
return true
}
})
setOnQueryTextFocusChangeListener { view, hasFocus ->
binding.appBar.setExpanded(!hasFocus)
if (hasFocus) {
binding.toolbar.apply {
setSupportActionBar(this)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
} else {
supportActionBar?.setDisplayHomeAsUpEnabled(false)
}
isSelected = hasFocus
}
}
}
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
if (ev?.action == MotionEvent.ACTION_DOWN) {
val view: View? = currentFocus
if (view is SearchView.SearchAutoComplete) {
val outRect = Rect()
view.getGlobalVisibleRect(outRect);
if (!outRect.contains(ev.rawX.toInt(), ev.rawY.toInt())) {
view.clearFocus()
val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
}
}
return super.dispatchTouchEvent(ev)
}
}
Mainfest.xml
<activity android:name="ExploreConsultationsActivity"
android:screenOrientation="portrait"
android:theme="#style/NoActionBar"/>
Style.xml
<style name="NoActionBar" parent="#style/Theme.MaterialComponents.Light.NoActionBar">
<item name="colorPrimary">#color/abc</item>
<item name="colorPrimaryDark">#color/xyz</item>
<item name="colorAccent">#color/abc</item>
<item name="android:theme">#style/AppTheme</item>
<item name="android:colorBackground">#color/white</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
<item name="android:statusBarColor">#color/status_bar</item>
<item name="android:windowLightStatusBar" tools:ignore="NewApi">true</item>
</style>
Actual Output
Expected Output
Image 1 and Image 2 please look top image of question.
Github Project
UPDATE
my search view is very close to status bar so how can I give top margin or padding?
You could change the start margin of the SearchView when it got the focus; and return it to the original margin when it loses the focus:
var originalMargin = 0
fun setupSearchView() {
binding.consultationSearchView.apply {
setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?) = false
override fun onQueryTextChange(newText: String?): Boolean {
if (newText != null) {
}
return true
}
})
val params =
binding.consultationSearchView.layoutParams as CollapsingToolbarLayout.LayoutParams
originalMargin = params.marginStart
setOnQueryTextFocusChangeListener { view, hasFocus ->
binding.appBar.setExpanded(!hasFocus)
isSelected = hasFocus
if (hasFocus)
params.marginStart = originalMargin + 150 // arbitrary constant
else
params.marginStart = originalMargin
view.layoutParams = params
}
}
}
I created a class called BaseActivity that all of the activities inherit from so that I can add the drawer layout in all of my activities.
The Drawer Toggle button is shown in the AppBar, but when I click on it nothing happens!
Here is the code for the BaseActivity.kt:
open class BaseActivity : AppCompatActivity() {
private var dl: DrawerLayout? = null
private var t: ActionBarDrawerToggle? = null
private var nv: NavigationView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.base_activity)
dl = findViewById(R.id.drawer_layout)
t = ActionBarDrawerToggle(this, dl, R.string.drawer_open, R.string.drawer_close)
supportActionBar?.setDisplayShowTitleEnabled(true);
supportActionBar?.setHomeButtonEnabled(true);
supportActionBar?.setDisplayHomeAsUpEnabled(true);
dl?.addDrawerListener(t!!)
t?.syncState()
nv = findViewById(R.id.navigation_view)
nv?.setNavigationItemSelectedListener(NavigationView.OnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.nav_note -> Toast.makeText(this#BaseActivity, "My Account", Toast.LENGTH_SHORT).show()
R.id.nav_calendar -> Toast.makeText(this#BaseActivity, "Settings", Toast.LENGTH_SHORT).show()
R.id.nav_trash -> Toast.makeText(this#BaseActivity, "Trash", Toast.LENGTH_SHORT).show()
else -> return#OnNavigationItemSelectedListener true
}
true
})
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return if (t?.onOptionsItemSelected(item) == true) {
true
} else super.onOptionsItemSelected(item!!)
}
}
and here the base_activity.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
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/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".BaseActivity">
<!--include layout="#layout/toolbar"/-->
<com.google.android.material.navigation.NavigationView
android:id="#+id/navigation_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:menu="#menu/drawer_menu"
app:headerLayout="#layout/nav_header"/>
</androidx.drawerlayout.widget.DrawerLayout>
For the style I left it at DarkActionBar. Here is how it looks like Don't mind what's written there
I still cannot figure out what's wrong and why it doesn't work. I appreciate any suggestions from the community. Thank you.
So I kind of changed my code to this and it is working Here is the new code for BaseActivty class
open class BaseActivity : AppCompatActivity() {
//var toolbar: Toolbar? = null
var drawerLayout: DrawerLayout? = null
var drawerToggle: ActionBarDrawerToggle? = null
var navigationView: NavigationView? = null
var mContext: Context? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mContext = this#BaseActivity
setContentView(R.layout.base_activity)
}
override fun setContentView(layoutResID: Int) {
val fullView = layoutInflater.inflate(R.layout.base_activity, null) as DrawerLayout
val activityContainer = fullView.findViewById<View>(R.id.activity_content) as FrameLayout
layoutInflater.inflate(layoutResID, activityContainer, true)
super.setContentView(fullView)
}
private fun setUpNav() {
drawerLayout = findViewById<View>(R.id.drawer_layout) as DrawerLayout
drawerToggle = ActionBarDrawerToggle(
this#BaseActivity,
drawerLayout,
R.string.app_name,
R.string.app_name
)
drawerLayout!!.setDrawerListener(drawerToggle)
supportActionBar!!.setHomeButtonEnabled(true)
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
navigationView = findViewById<View>(R.id.navigation_view) as NavigationView
// Setting Navigation View Item Selected Listener to handle the item
// click of the navigation menu
navigationView!!.setNavigationItemSelectedListener(NavigationView.OnNavigationItemSelectedListener { menuItem -> // Checking if the item is in checked state or not, if not make
// it in checked state
if (menuItem.isChecked) menuItem.isChecked = false else menuItem.isChecked = true
// Closing drawer on item click
drawerLayout!!.closeDrawers()
// Check to see which item was being clicked and perform
// appropriate action
val intent_calendar = Intent(this, CalendarActivity::class.java)
val intent_add_note = Intent(this, AddActivity::class.java)
val intent_note = Intent(this, MainActivity::class.java)
when (menuItem.itemId) {
R.id.nav_note -> this.startActivity(intent_note)
R.id.nav_calendar -> this.startActivity(intent_calendar)
R.id.nav_trash -> Toast.makeText(this, "Trash", Toast.LENGTH_SHORT).show()
R.id.nav_add_note -> this.startActivity(intent_add_note)
else -> return#OnNavigationItemSelectedListener true
}
false
})
// calling sync state is necessay or else your hamburger icon wont show
// up
drawerToggle!!.syncState()
}
public override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
setUpNav()
drawerToggle!!.syncState()
}
override fun onConfigurationChanged(newConfig: Configuration) {
if (newConfig != null) {
super.onConfigurationChanged(newConfig)
}
drawerToggle!!.onConfigurationChanged(newConfig)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return if (drawerToggle?.onOptionsItemSelected(item) == true) {
true
} else super.onOptionsItemSelected(item!!)
}
}
and here is the code for its xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
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/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".BaseActivity"
>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="#+id/activity_content"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<com.google.android.material.navigation.NavigationView
android:id="#+id/navigation_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:menu="#menu/drawer_menu"
app:headerLayout="#layout/nav_header"/>
</androidx.drawerlayout.widget.DrawerLayout>
Here is the answer that helped me, (I didn't use a toolbar and i left the style to "Theme.AppCompat.Light.DarkActionBar") https://stackoverflow.com/a/42533759/15018682
I am trying to add a SearchView to my custom Toolbar.
I've tried adding the SearchView directly to the Toolbar with toolbar.addView(searchView) but it didn't change anything, I am getting a blank Toolbar no matter what I try.
What am I doing wrong? this should be simple...
Toolbar:
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.Toolbar 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="wrap_content"
android:elevation="4dp"
android:theme="#style/ThemeOverlay.AppCompat.Light"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light">
</androidx.appcompat.widget.Toolbar>
Search Menu:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/action_search"
android:icon="#drawable/ic_search"
android:title="Search"
app:actionViewClass="androidx.appcompat.widget.SearchView"
app:showAsAction="ifRoom|collapseActionView" />
</menu>
Fragment:
lateinit var toolbar: Toolbar
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
view = ConstraintLayout(this.context)
view.layoutParams = ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.MATCH_PARENT, ConstraintLayout.LayoutParams.MATCH_PARENT)
toolbar = inflater.inflate(R.layout.toolbar, container, false) as Toolbar
setHasOptionsMenu(true)
return view
}
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
super.onCreateOptionsMenu(menu, inflater)
// Inflate the options menu from XML
inflater?.inflate(R.menu.search_menu, menu)
val searchItem = menu?.findItem(R.id.action_search)
val searchView = searchItem?.actionView as SearchView
searchView.imeOptions = EditorInfo.IME_ACTION_DONE
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(p0: String?): Boolean {
return false
}
override fun onQueryTextChange(p0: String?): Boolean {
//adapter.getFilter().filter(newText);
return false
}
})
toolbar.addView(searchView)
}
Solved it with adding setSupportActionBar(toolbar) in onCreateView()
I'm using a BottomNavigationView in my app. Right now my navigation view looks like this:
but I want it to be with underlined selected item, like this:
Are there any ways to do this with some standard attributes?
You can do that using a SpannableString with UnderlineSpan to the item title when this item is selected by the user by setting OnNavigationItemSelectedListener listener to the BottomNavigationView
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
BottomNavigationView bottomNavigationView = (BottomNavigationView)
findViewById(R.id.bottom_navigation);
underlineMenuItem(bottomNavigationView.getMenu().getItem(0)); // underline the default selected item when the activity is launched
bottomNavigationView.setOnNavigationItemSelectedListener(
new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
removeItemsUnderline(bottomNavigationView); // remove underline from all items
underlineMenuItem(item); // underline selected item
switch (item.getItemId()) {
// handle item clicks
}
return false;
}
});
}
private void removeItemsUnderline(BottomNavigationView bottomNavigationView) {
for (int i = 0; i < bottomNavigationView.getMenu().size(); i++) {
MenuItem item = bottomNavigationView.getMenu().getItem(i);
item.setTitle(item.getTitle().toString());
}
}
private void underlineMenuItem(MenuItem item) {
SpannableString content = new SpannableString(item.getTitle());
content.setSpan(new UnderlineSpan(), 0, content.length(), 0);
item.setTitle(content);
}
This works exactly if you're using text based items, but in your case you're just using icons in your menu, and to resolve this issue; you have to utilize the android:title of menu items in menu.xml with white spaces as follows
bottom_nav_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/action_favorites"
android:enabled="true"
android:icon="#drawable/ic_favorite_white_24dp"
android:title="#string/text_spaces"
app:showAsAction="ifRoom" />
<item
android:id="#+id/action_schedules"
android:enabled="true"
android:icon="#drawable/ic_access_time_white_24dp"
android:title="#string/text_spaces"
app:showAsAction="ifRoom" />
<item
android:id="#+id/action_music"
android:enabled="true"
android:icon="#drawable/ic_audiotrack_white_24dp"
android:title="#string/text_spaces"
app:showAsAction="ifRoom" />
</menu>
And use in your text as many times as you need spaces which will reflect on the length of the the line under each item
strings.xml
<resources>
...
<string name="text_spaces"> </string>
This is a preview
hope this solves your issue, and happy for any queries.
I know I'm late to the party, but for the next generations - this is a solution with more control + animation:) using constraint layout.
the example is for 4 items, adjust the numbers.
first, create a view with the desired characteristics in the (constraint) layout that contains the BottomNavigationView. set app:layout_constraintWidth_percent to 1/number of items
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/mainTabBottomNavigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="#android:color/white"
android:nestedScrollingEnabled="true"
app:elevation="16dp"
app:itemIconTint="#drawable/nav_account_item"
app:labelVisibilityMode="unlabeled"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="#menu/main_bottom_navigation"
/>
<View
android:id="#+id/underline"
android:layout_width="0dp"
android:layout_height="3dp"
android:background="#color/underlineColor"
android:elevation="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintWidth_percent="0.25" />
</androidx.constraintlayout.widget.ConstraintLayout>
Then, use this function inside OnNavigationItemSelectedListener:
private fun underlineSelectedItem(view: View, itemId: Int) {
val constraintLayout: ConstraintLayout = view as ConstraintLayout
TransitionManager.beginDelayedTransition(constraintLayout)
val constraintSet = ConstraintSet()
constraintSet.clone(constraintLayout)
constraintSet.setHorizontalBias(
R.id.underline,
getItemPosition(itemId) * 0.33f
)
constraintSet.applyTo(constraintLayout)
}
complete code (inside a fragment):
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val navController = Navigation.findNavController(
requireActivity(),
R.id.mainNavigationFragment
)
mainTabBottomNavigation.setupWithNavController(navController)
underlineSelectedItem(view, R.id.bottomNavFragmentHome) //select first item
mainTabBottomNavigation.setOnNavigationItemSelectedListener { item ->
underlineSelectedItem(view, item.itemId)
true
}
}
private fun underlineSelectedItem(view: View, itemId: Int) {
val constraintLayout: ConstraintLayout = view as ConstraintLayout
TransitionManager.beginDelayedTransition(constraintLayout)
val constraintSet = ConstraintSet()
constraintSet.clone(constraintLayout)
constraintSet.setHorizontalBias(
R.id.underline,
getItemPosition(itemId) * 0.33f
)
constraintSet.applyTo(constraintLayout)
}
private fun getItemPosition(itemId: Int): Int {
return when (itemId) {
R.id.bottomNavFragmentHome -> 0
R.id.bottomNavFragmentMyAccount -> 1
R.id.bottomNavFragmentCoupon -> 2
R.id.bottomNavFragmentSettings -> 3
else -> 0
}
}
Notice that this implementation overrides the navigation functionality.
In order to maintain this functionality, you'll need to use NavigationUI.onNavDestinationSelected(item, navController)
at the end of the transition animation.
complete code:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val navController = Navigation.findNavController(
requireActivity(),
R.id.mainNavigationFragment
)
mainTabBottomNavigation.setupWithNavController(navController)
underlineSelectedItem(view, R.id.bottomNavFragmentHome, null, null, null)
mainTabBottomNavigation.setOnNavigationItemSelectedListener { item ->
underlineSelectedItem(view, item.itemId, item, navController) { item1, navController1 ->
safeLet(item1, navController1) { a, b->
NavigationUI.onNavDestinationSelected(a, b)
}
}
true
}
}
private fun underlineSelectedItem(
view: View,
itemId: Int,
item: MenuItem?,
navController: NavController?,
onAnimationEnd: ((item: MenuItem?, navController: NavController?) -> Unit)?
) {
val constraintLayout: ConstraintLayout = view as ConstraintLayout
val transition: Transition = ChangeBounds()
transition.addListener(object : Transition.TransitionListener {
override fun onTransitionStart(transition: Transition?) {
}
override fun onTransitionEnd(transition: Transition?) {
onAnimationEnd?.invoke(item, navController)
}
override fun onTransitionCancel(transition: Transition?) {
}
override fun onTransitionPause(transition: Transition?) {
}
override fun onTransitionResume(transition: Transition?) {
}
})
TransitionManager.beginDelayedTransition(constraintLayout, transition)
val constraintSet = ConstraintSet()
constraintSet.clone(constraintLayout)
constraintSet.setHorizontalBias(
R.id.underline,
getItemPosition(itemId) * 0.33f
)
constraintSet.applyTo(constraintLayout)
}
private fun getItemPosition(itemId: Int): Int {
return when (itemId) {
R.id.bottomNavFragmentHome -> 0
R.id.bottomNavFragmentMyAccount -> 1
R.id.bottomNavFragmentCoupon -> 2
R.id.bottomNavFragmentSettings -> 3
else -> 0
}
}
(safeLet is a Kotlin helper function for checking two variables nullabilty:
fun <T1 : Any, T2 : Any, R : Any> safeLet(p1: T1?, p2: T2?, block: (T1, T2) -> R?): R? {
return if (p1 != null && p2 != null) block(p1, p2) else null
}
)
final result:
This could be a simpler & better solution than my other answer; it also could have a variety of capabilities like the thickness of the width/height, corners, shapes, padding..etc stuff of drawable capabilities.
You can create a selector (only with a checked state) that has a gravity set to the bottom:
item_background.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true">
<layer-list>
<item android:gravity="bottom|center_horizontal">
<shape android:shape="rectangle">
<size android:width="100dp" android:height="5dp" />
<solid android:color="#03DAC5" />
<corners android:bottomLeftRadius="3dp" android:bottomRightRadius="3dp" />
</shape>
</item>
</layer-list>
</item>
</selector>
Set this to app:itemBackground:
<com.google.android.material.bottomnavigation.BottomNavigationView
....
app:itemBackground="#drawable/item_background"
I've looked around and haven't been able to find exactly what I'm looking for. A little help would be appreciated. I'm attempting to implementing a SearchWidget as shown here. I'm getting a bizarre setup however. The search Icon is not even showing up, on the far right there is three vertical dots as part of the toolbar, and when I click on those a Search box appears. But clicking on that doesn't register anything through setOnClickListener or setOnQueryTextFocusChangeListener. Any help would be much appreciated. Like so:
toolbar when opening the app
popup search menu - doesn't do anything when I click on it
Here's what I've got
My SearchActivity:
class SearchCategoryActivity : MvvmActivity<SearchCategoryViewModel>() {
companion object {
private const val CATEGORY = "category"
fun newIntent(context: Context): Intent {
return Intent(context, SearchCategoryActivity::class.java)
}
}
#Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
val listofCategories : List<Category>? = null
private lateinit var adapter: CategoryGroupAdapter
var browsingData : List<Category>? = null
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_search_category)
setSupportActionBar(toolbar)
val actionBar = supportActionBar
actionBar?.setDisplayHomeAsUpEnabled(true)
adapter = CategoryGroupAdapter(this)
adapter.setOnClickListener { category, _ -> onCategoryClick(category) }
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.itemAnimator = DefaultItemAnimator()
recyclerView.adapter = adapter
toolbar.setNavigationOnClickListener { finish() }
//clearImageView.setOnClickListener { searchEdiText.text = null }
addFab.setOnClickListener { onAddFabClick() }
viewModel.loadCategories.subscribe(this, object : FlowableSubscriber<List<Category>> {
override fun onNext(data: List<Category>) {
browsingData = data
onLoadCategories(data)
}
override fun onComplete() {
Timber.error { "onComplete" }
}
override fun onError(error: Throwable) {
onLoadCategoriesFailed(error)
}
})
LceAnimator.showLoading(loading, content, error)
viewModel.loadCategories()
System.out.println("Here")
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the options menu from XML
val inflater = menuInflater
inflater.inflate(R.menu.menu_search, menu)
// Get the SearchView and set the searchable configuration
val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager
val searchView = menu.findItem(R.id.action_search).actionView as SearchView?
// Assumes current activity is the searchable activity
searchView?.setSearchableInfo(searchManager.getSearchableInfo(componentName))
searchView?.setIconifiedByDefault(false) // Do not iconify the widget; expand it by default
searchView?.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
System.out.println("clicked")
}
})
searchView?.setOnQueryTextFocusChangeListener(object : View.OnFocusChangeListener {
override fun onFocusChange(v: View?, hasFocus: Boolean) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
})
My SearchActivity.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/background">
<!-- Dummy item to prevent AutoCompleteTextView from receiving focus -->
<!-- :nextFocusUp and :nextFocusLeft have been set to the id of this component
to prevent the dummy from receiving focus again -->
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/ThemeOverlay.AppCompat.Dark">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize">
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<include layout="#layout/layout_loading" />
<include layout="#layout/layout_search" />
<include layout="#layout/layout_error" />
</FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
My searchable.xml:
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="#string/app_name"
android:hint="#string/search_hint" />
My search_menu.xml:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/action_search"
android:actionViewClass="android.widget.SearchView"
android:layout_width="match_parent"
android:icon="#android:drawable/ic_search_category_default"
android:showAsAction="always"
android:title="#string/search"
app:queryBackground="#color/background"/>
</menu>
Try adding app instead of android and using v7:
app:actionViewClass="android.support.v7.widget.SearchView"
Also, to make it collapsable:
app:showAsAction="always|collapseActionView"
And no need for android:layout_width="match_parent".
You can try this so it fill would fill the toolbar
searchMenuItem.actionView.also {
it.post {
it.layoutParams = it.layoutParams.apply {
width = ViewGroup.LayoutParams.MATCH_PARENT
}
}
}