How to use Appbar with toolbar in android - android

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
}
}
}

Related

Android overflow menu text not appearing

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"

android BottomNavigationView underline item

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"

BottomNavigationView hides while scrolling but not displaying when fragment is changed

I have a BottomNavigationView which hides when the view is getting scrolled to the bottom and returns when it's scrolled to the top.
My issue is when the BottomNavigationView is hidden and I change
the fragment I am unable to show the BottomNavigationView in the
screen any help is appreciable.
Below is my code of DashboardActivity.kt
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:fitsSystemWindows="true"
app:elevation="2dp">
<include layout="#layout/toolbar"/>
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:id="#+id/frame_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
<android.support.design.widget.BottomNavigationView
android:id="#+id/navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="0dp"
android:layout_marginStart="0dp"
android:layout_gravity="bottom"
app:labelVisibilityMode="selected"
app:layout_behavior="#string/hide_bottom_view_on_scroll_behavior"
app:itemIconTint="#android:color/white"
app:itemTextColor="#android:color/white"
android:background="#color/colorAccent"
app:itemBackground="#color/colorAccent"
app:menu="#menu/navigation"/>
Any help is appreciable
Thanks in advance
Edit #1
DashboardActivity.kt
class DashboardActivity : BaseActivity(), EditProfileFragment.RefreshView, AddPatientFragment.NavigateView {
override fun navigateView() {
toDashboard()
}
override fun refreshView() {
val f: Fragment? = supportFragmentManager.findFragmentById(R.id.frame_container)
if (f != null && f is ProfileFragment) {
f.refreshView()
supportActionBar?.title = PROFILE
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_dashboard)
initializeToolBar()
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
toDashboard()
}
/**
* Initialization of ToolBar
*/
private fun initializeToolBar() {
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
if (supportActionBar != null) {
supportActionBar!!.setDisplayShowHomeEnabled(true)
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
}
}
private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.navigation_home -> {
toDashboard()
return#OnNavigationItemSelectedListener true
}
R.id.navigation_medicine -> {
toMedicines()
return#OnNavigationItemSelectedListener true
}
R.id.navigation_add_patient -> {
toAddPatient()
return#OnNavigationItemSelectedListener true
}
R.id.navigation_profile -> {
toProfile()
return#OnNavigationItemSelectedListener true
}
}
false
}
private fun toProfile() {
toggleToolBar(false)
// navigation.selectedItemId = R.id.navigation_profile
navigation.labelVisibilityMode = View.VISIBLE
replaceFragment(ProfileFragment::class.java, PROFILE, false, null, R.id.frame_container)
}
private fun toAddPatient() {
toggleToolBar(false)
// navigation.selectedItemId = R.id.navigation_add_patient
navigation.labelVisibilityMode = View.VISIBLE
replaceFragment(AddPatientFragment::class.java, ADD_PATIENT, false, null, R.id.frame_container)
}
private fun toMedicines() {
toggleToolBar(false)
// navigation.selectedItemId = R.id.navigation_medicine
navigation.labelVisibilityMode = View.VISIBLE
replaceFragment(MedicineFragment::class.java, MEDICINE, false, null, R.id.frame_container)
}
private fun toDashboard() {
toggleToolBar(false)
navigation.labelVisibilityMode = View.VISIBLE
// navigation.selectedItemId = R.id.navigation_home
replaceFragment(HomeFragment::class.java, HOME, false, null, R.id.frame_container)
}
private fun toggleToolBar(value: Boolean) {
supportActionBar?.setDisplayHomeAsUpEnabled(value)
supportActionBar?.setDisplayShowHomeEnabled(value)
}
}
Update to latest Support Library Version 28.0.0-alpha1 and just add one attribute to BottomNavigationView.
<BottomNavigationView
....
....
app:layout_behavior="#string/hide_bottom_view_on_scroll_behavior"/>
Note :- see here Hide/Show bottomNavigationView on Scroll

SearchView not filling entire Actionbar by default, and not responding to clicking

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
}
}
}

Keyboard hides BottomSheetDialogFragment

There are more fields below the keyboard. This happened when i updated the support library. I know it's Kotlin but it looks almost the same as java. How do I fix this issue?
This is what it looks like:
My code:
class ProjectsEditBottomSheetFragment(val privateID: String,
val publicID: String) : BottomSheetDialogFragment() {
private val mBottomSheetBehaviorCallback = object : BottomSheetBehavior.BottomSheetCallback() {
override fun onStateChanged(bottomSheet: View, newState: Int) {
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
dismiss()
}
}
override fun onSlide(bottomSheet: View, slideOffset: Float) {
if (slideOffset < -0.15f) {
dismiss()
}
}
}
override fun setupDialog(dialog: Dialog, style: Int) {
super.setupDialog(dialog, style)
val view = View.inflate(context, R.layout.projects_edit_sheet, null)
dialog.setContentView(view)
dialog.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
val params = (view.parent as View).layoutParams as CoordinatorLayout.LayoutParams
val behavior = params.behavior
if (behavior != null && behavior is BottomSheetBehavior<*>) {
behavior.setBottomSheetCallback(mBottomSheetBehaviorCallback)
}
// Get and set values
val realm = Realm.getDefaultInstance()
val realmObject = realm.where(ProjectsRealmObject::class.java)
.equalTo("privateID", privateID)
.findFirst()
realm.beginTransaction()
view.title_input.text = SpannableStringBuilder(realmObject.title)
view.description_input.text = SpannableStringBuilder(realmObject.description)
view.public_checkbox.isChecked = realmObject.isPublic
realm.commitTransaction()
// Keyboard
view.title_input.onFocusChangeListener = View.OnFocusChangeListener { _, hasFocus ->
if (hasFocus) {
(context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).showSoftInput(view.title_input, InputMethodManager.SHOW_FORCED)
} else {
(context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).hideSoftInputFromWindow(view.title_input.windowToken, 0)
}
}
view.description_input.onFocusChangeListener = View.OnFocusChangeListener { _, hasFocus ->
if (hasFocus) {
(context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).showSoftInput(view.description_input, InputMethodManager.SHOW_FORCED)
} else {
(context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).hideSoftInputFromWindow(view.description_input.windowToken, 0)
}
}
// Click listners
view.public_layout.setOnClickListener { view.public_checkbox.toggle() }
view.cancel.setOnClickListener {
view?.hideKeyboard()
dismiss()
}
view.save.setOnClickListener {
view?.hideKeyboard()
// Save to realm
realm.beginTransaction()
realmObject.title = if (view.title_input.text.toString() == "") getString(R.string.unnamed) else view.title_input.text.toString()
realmObject.description = view.description_input.text.toString()
realmObject.isPublic = view.public_checkbox.isChecked
realmObject.synced = false
realmObject.updatedRealm = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()).toString() + ""
realm.commitTransaction()
ProjectsSync(context)
toast("Sparat")
dismiss()
}
}
}
xml:
<ScrollView
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:background="#color/white"
app:layout_collapseMode="none"
app:behavior_hideable="false"
app:behavior_peekHeight="100dp"
app:layout_behavior="android.support.design.widget.BottomSheetBehavior"
style="#style/Widget.Design.BottomSheet.Modal">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="#+id/content">
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingRight="16dp"
android:paddingLeft="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="8dp">
<android.support.design.widget.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/edit_info_placeholder_title"
android:id="#+id/title_input"/>
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingRight="16dp"
android:paddingLeft="16dp">
<android.support.design.widget.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/edit_info_placeholder_description"
android:id="#+id/description_input"/>
</android.support.design.widget.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:clickable="true"
android:background="#drawable/click"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:id="#+id/public_layout">
<android.support.v7.widget.AppCompatCheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:id="#+id/public_checkbox"
android:layout_marginRight="8dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/edit_info_placeholder_is_public"
android:layout_gravity="center_vertical"
style="#style/textMedium"/>
</LinearLayout>
<!-- Buttons -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="right"
android:paddingBottom="8dp">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/edit_info_button_cancel"
android:id="#+id/cancel"
style="#style/Widget.AppCompat.Button.Borderless.Colored"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/edit_info_button_save"
android:id="#+id/save"
style="#style/Widget.AppCompat.Button.Borderless.Colored"/>
</LinearLayout>
</LinearLayout>
</FrameLayout>
I found the solution for 27 api. So the reason why keyboard hides view even with SOFT_INPUT_ADJUST_RESIZE is that the windowIsFloating is set for Dialogs.
The most convenient way that I found to change this is by creating style:
<style name="DialogStyle" parent="Theme.Design.Light.BottomSheetDialog">
<item name="android:windowIsFloating">false</item>
<item name="android:statusBarColor">#android:color/transparent</item>
<item name="android:windowSoftInputMode">adjustResize</item>
</style>
And set this in onCreate method of your BottomSheetDialogFragment:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(DialogFragment.STYLE_NORMAL, R.style.DialogStyle)
}
This is how it looks on my device:
I tried all of answers in this topic but nothing helped. I looked through many sites and found only one solution that working for me.
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
dialog.setOnShowListener {
Handler().post {
val bottomSheet = (dialog as? BottomSheetDialog)?.findViewById<View>(R.id.design_bottom_sheet) as? FrameLayout
bottomSheet?.let {
BottomSheetBehavior.from(it).state = BottomSheetBehavior.STATE_EXPANDED
}
}
}
return dialog
}
Original solution
You can use the next class:
import android.graphics.Rect;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.BottomSheetBehavior;
import android.support.design.widget.BottomSheetDialog;
import android.support.design.widget.BottomSheetDialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
public class TestBottomSheetDialog extends BottomSheetDialogFragment {
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View fragmentView = LayoutInflater.from(getContext()).inflate(R.layout.fragment_bottom_sheet, container, false);
if (getDialog().getWindow() != null) {
getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
}
if (getActivity() != null) {
View decorView = getActivity().getWindow().getDecorView();
decorView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
Rect displayFrame = new Rect();
decorView.getWindowVisibleDisplayFrame(displayFrame);
int height = decorView.getContext().getResources().getDisplayMetrics().heightPixels;
int heightDifference = height - displayFrame.bottom;
if (heightDifference != 0) {
if (fragmentView.getPaddingBottom() != heightDifference) {
fragmentView.setPadding(0, 0, 0, heightDifference);
}
} else {
if (fragmentView.getPaddingBottom() != 0) {
fragmentView.setPadding(0, 0, 0, 0);
}
}
});
}
getDialog().setOnShowListener(dialog -> {
BottomSheetDialog d = (BottomSheetDialog) dialog;
View bottomSheetInternal = d.findViewById(android.support.design.R.id.design_bottom_sheet);
if (bottomSheetInternal == null) return;
BottomSheetBehavior.from(bottomSheetInternal).setState(BottomSheetBehavior.STATE_EXPANDED);
});
return fragmentView;
}
}
This is working for me
public class CustomBottomSheetDialogFragment extends BottomSheetDialogFragment
{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle
savedInstanceState) {
View v = inflater.inflate(R.layout.content_dialog_bottom_sheet, container, false);
getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
return v;
}
This solution worked for me after spending 5 hours without luck:
Step #1:
Add this code to your styles.xml (located in res\values folder)
<style name="CustomizedBottomDialogStyle">
<item name="android:windowBackground">#android:color/transparent</item>
<item name="android:backgroundDimEnabled">true</item>
<item name="android:backgroundDimAmount">0.7</item>
<item name="android:windowIsFloating">false</item>
<item name="android:windowAnimationStyle">#android:style/Animation.Dialog</item>
<item name="android:statusBarColor">#android:color/transparent</item>
<item name="android:windowSoftInputMode">adjustResize</item>
<item name="android:background">#android:color/transparent</item>
</style>
The key here is to set android:windowIsFloating -> false, if it is true your code will not work! Therefor i used rather android:backgroundDimEnabled and android:backgroundDimAmount to make background looks transparent with beautiful overlay.
Step #2:
Write this function to adjust it programmatically (note it is not optional, you need to perform both steps #1 and #2):
private fun showDialog() {
BottomSheetDialog(requireActivity(), R.style.CustomizedBottomDialogStyle).apply {
window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
setOnShowListener {
Handler().post {
val bottomSheet = findViewById<View>(R.id.design_bottom_sheet) as? FrameLayout
bottomSheet?.let {
BottomSheetBehavior.from(it).state = STATE_EXPANDED
}
}
}
setContentView(R.layout.dialog_layout)
// Your code goes here....
show()
}
}
The answer to the highest score is partly right. In my case, 90% of the view is visible after the same style is set. Finally, I made it completely visible through the following solution:
editText.setOnFocusChangeListener { v, hasFocus ->
if (hasFocus) {
(this#****BottomSheetDialogFragment.dialog as BottomSheetDialog).behavior.state = BottomSheetBehavior.STATE_EXPANDED
}
}
add this to your styles
<style name="DialogStyle">
<item name="android:windowBackground">#android:color/transparent</item>
<item name="colorPrimaryDark">#android:color/transparent</item>
</style>
then in your's bottom sheet dialog's onCreate() add
setStyle(DialogFragment.STYLE_NO_FRAME, R.style.DialogStyle);
also don't forget to add to dialog's setupDialog() method
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);

Categories

Resources