I have a simple screen implemented with Jetpack Compose,
here is a preview of the screen.
There is a button like the sun on the TopAppBar which the user clicks to take a screenshot.
The problem is that I want to show the screenshot of the entire screen but without showing the status bar and the navigation bar.
Is there a solution to do this?
The composables are tied to a View which you can access with LocalView.current and through that View you can retrieve its .context and through that you can get to its Activity and to the Window object.
Once you have a reference to the Window (and to the View, if you have to support API level lower than 30), you can control status bar and navigation bar visibility.
A code example:
#Composable
fun MyFullScreenComposable() {
val view = LocalView.current
SideEffect {
requestFullScreen(view)
}
Box {
// ... other content
Button(
onClick = { requestFullScreen(view) }
) {
Text("Go fullscreen")
}
}
}
fun requestFullScreen(view: View) {
// !! should be safe here since the view is part of an Activity
val window = view.context.getActivity()!!.window
WindowCompat.getInsetsController(window, view).hide(
WindowInsetsCompat.Type.statusBars() or
WindowInsetsCompat.Type.navigationBars()
)
}
fun Context.getActivity(): Activity? = when (this) {
is Activity -> this
// this recursion should be okay since we call getActivity on a view context
// that should have an Activity as its baseContext at some point
is ContextWrapper -> baseContext.getActivity()
else -> null
}
In your case you might have to hide your action/title bar as well (or maybe you are already doing that).
Note that when the user taps on the screen while in full-screen mode, the bars will slide back. That's why I added a button as an example how you can let the user go back into full-screen mode through an action. In your case that could be a FloatingActionButton or some action on a semi-transparent top action bar.
Related
I have a form with a couple TextViews and a couple TextInputLayouts. The TextViews have onClickListeners which when clicked launch a modal bottom sheet with multiple choices. When choice is selected, then bottom sheet should be closed and then next TextView should be focused which should automatically call the second bottom sheet to select a value for the second TextView.
The bottom sheet changes a value in ViewModel. The main fragment has an observer, and when its value is changes, it runs a function that sets focus on the second TextView and that should launch the second bottom sheet fragment. But I get an error:
Navigation action/destination com.myapplications.simpleexpensetracker:id/action_addExpenseHomeFragment_to_addExpenseBottomSheetExpenseCategory cannot be found from the current destination Destination(com.myapplications.simpleexpensetracker:id/addExpenseBottomSheetAccountCategory) label=fragment_add_expense_bottom_sheet_account_category
HomeFragment
binding.tvAccountCategoryValue.setOnFocusChangeListener { v, hasFocus ->
if (hasFocus){
v.performClick()
}
}
binding.tvExpenseCategoryValue.setOnFocusChangeListener { v, hasFocus ->
if (hasFocus){
v.performClick()
}
}
...
binding.tvAccountCategoryValue.setOnClickListener {
findNavController().navigate(R.id.action_addExpenseHomeFragment_to_addExpenseBottomSheetAccountCategory)
}
...
addExpenseViewModel.account.observe(viewLifecycleOwner){ account ->
if (!account.isNullOrBlank()){
binding.tvAccountCategoryValue.text = account
selectNextField(binding.tvAccountCategoryValue)
} else {
binding.tvAccountCategoryValue.text = resources.getString(R.string.hint_account_category)
}
}
...
private fun selectNextField(view : View){
when(view){
binding.tvAccountCategoryValue -> {
binding.tvExpenseCategoryValue.requestFocus()
}
binding.tvExpenseCategoryValue -> binding.tilAmountValue.requestFocus()
}
}
BottomSheetDialog part which closes the fragment(both are the same)
addExpenseViewModel.accountCategories.observe(viewLifecycleOwner){ accountCategories ->
if (accountCategories.isNotEmpty()){
binding.rvAccountCategories.adapter = AccountCategoriesAdapter(accountCategories){ accountCategoryId ->
addExpenseViewModel.setAccountCategory(accountCategoryId)
activity?.onBackPressed()
}
binding.rvAccountCategories.visibility = View.VISIBLE
} else {
binding.rvAccountCategories.visibility = View.GONE
}
}
I'm not even sure what to try to be honest... I've tried using onBackPressed additionally in the home fragment, it then seemingly focuses on the textview (judged by a background change that I have implemented with a selector), and then triggers the onClickListener, but the navController does not launch the next bottomsheet fragment. Maybe the way I close the first bottom sheet with activity?.onBackPressed() is the problem somehow?
I am trying to create a list of items with a hidden button at the bottom. By default the button is not visible on the screen.
If a user scrolls to the bottom of the list the button should appear like it is the last item of the list.
I created illustrations to better visualize the desired behaviour:
Sketch 1: List with a few items
Sketch 2: List with a lot of items
I already tried the solution for a similar problem (https://stackoverflow.com/a/69196765/11720296) and added extra offset but unfortunately it didn't work.
Does somebody have an idea how to create this behaviour?
itemsIndexed will help in this case in following example I have used itemsIndexed in Lazycolumn it gives index which is visible to the user.
In this example I was trying to show some specific text only when first message is visible.
LazyColumn() {
itemsIndexed(tempListOfMessages) { index, localMessage ->
if (localMessage != null) {
SingleMessageView(
localMessageInfo = localMessage,
(index == 0)
)
}
}
}
when scroll that text scroll with first item.
Try this.
Scaffold { paddingValues ->
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = paddingValues
) {
...
}
}
i've a problem in my Android app.
I have a fragment that is generated as a list of editable textview. Every textview has a setOnFocusChangeListener that calls an API on server.
FocusChangedListener works correctly on last textview, and after that my textview remain focused as you can see in the figure below.
Problem
Now i have to change menu (fragment) by clicking on the right menu button.
When button is clicked and the new menu is about to be loaded, my textview loses focus and calls the API, but i don't want my app do that. If I click menu button is because i've to change it and i expect that my old fragment disappear or basically don't execute the FocusChangedListener.
Any ideas?
I made a little trick to solve the problem.
I just added this code inside OnCreate of my DrawerMenu and then i set a Global Variable "drawerOpened" to check every time if my Menu is open or not.
// Initialize the action bar drawer toggle instance
val drawerToggle:ActionBarDrawerToggle = object : ActionBarDrawerToggle(
this,
drawer_layout,
toolbar,
R.string.navigation_drawer_open,
R.string.navigation_drawer_close
){
override fun onDrawerClosed(view:View){
super.onDrawerClosed(view)
GlobalVar.drawerOpened = false
}
override fun onDrawerOpened(drawerView: View){
super.onDrawerOpened(drawerView)
GlobalVar.drawerOpened = true
currentFocus?.clearFocus() //clear focus - any view having focus
}
}
Then in my HomeFragment i did this:
// if GlobalVar.drawerOpened is false then setOnFocus
if (field.validatefield || field.nextpage != 0)
{
textView.setOnFocusChangeListener { _, hasFocus ->
if (!hasFocus && !GlobalVar.drawerOpened)
{
if ((field.mandatory && !textView.text.toString().isNullOrEmpty()) || (!field.mandatory)) {
if (field.validatefield) {
validateField(field.fieldcode, textView.text.toString(), field.nextpage)
} else {
_goToNextPageNo = field.nextpage
goToNextPageIfExist()
}
}
}
}
}
This is not a complete solution, but it works fine.
I assume that after a click, the application switches to touch mode and your EditText loses focus.
It happens at the system level.
A focus can have only one view at a time.
https://android-developers.googleblog.com/2008/12/touch-mode.html
I am designing an app that has 3 button in main activity, and several buttons in a fragment. I want to change the color of a button in the fragment, depending on which button of main activity is toggled.
color1.setOnClickListener {
brush_chosen = 1
color1.setBackgroundColor(R.color.black)
color2.setBackgroundColor(0x00000000)
color3.setBackgroundColor(0x00000000)
if (frag_num == 8 ){
frag_8p.set_frag_value(frag_num,brush_chosen)
}
}
The function set_frag_value is :
fun set_frag_value(frag_num:Int,brush:Int) : Int
{
brush_chosen=brush
return brush
}
This change the value of brush_chosen. Then I made a function :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
ib0.setOnClickListener { view ->
Log.d("brush_color","Brush of 0 : "+brush_chosen)
if (brush_chosen==1)
{
Log.d("brush_color","Brush Confirm : "+brush_chosen)
DrawableCompat.setTint(ib0.drawable, ContextCompat.getColor(requireContext(),R.color.rndcolor1))
}
else if (brush_chosen==2)
{
Log.d("brush_color","Brush Confirm : "+brush_chosen)
DrawableCompat.setTint(ib0.drawable, ContextCompat.getColor(requireContext(),R.color.purple_500))
}
else if (brush_chosen==3)
{
Log.d("brush_color","Brush Confirm : "+brush_chosen)
DrawableCompat.setTint(ib0.drawable, ContextCompat.getColor(requireContext(),R.color.teal_200))
}
Log.d("brush_color","End of onclicklistener ")
}
}
I checked the log and theoretically this code should work correctly. However, I found that the button color did not change properly, even I checked my app prints all log correctly. For example, when I clicked button color1 in main activity, variable brush_chosen becomes 1 and the first button in fragment I clicked changes its color. But the second button I clicked does not change its color.
Is there any problem on my code using DrawableCompat ??
Android does some Drawable state caching under the hood. You might need to call mutate() on the Drawable you want to tint and then set the new Drawable in order for the tint to show up properly.
We are using the calls page with a picture in picture(pip) feature.
Problem :
In case user dismiss the pip popup window (by drag down to dismiss) we not able to detect that.
How to detect picture in picture window drag down to dismiss?
Update:
Set picture in picture
// RemoteRenderLayout -> call preview layout. You can set anyone view
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Rational aspectRatio = new Rational(
remoteRenderLayout.getWidth(), remoteRenderLayout.getHeight());
PictureInPictureParams params = new PictureInPictureParams.Builder()
.setAspectRatio(aspectRatio)
.build();
enterPictureInPictureMode(params);
}
else{
enterPictureInPictureMode();
}
When the activity enters or exits picture-in-picture mode the system calls Activity.onPictureInPictureModeChanged() or Fragment.onPictureInPictureModeChanged().
You can implement your code inside this:
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean,
newConfig: Configuration) {
if (isInPictureInPictureMode) {
// Hide the full-screen UI (controls, etc.) while in picture-in-picture mode.
} else {
// Restore the full-screen UI.
}
}