setOnClickListener is not showing in intellij - kotlin

setOnClickListener is not showing in intellij even the textview names I can't write them without R.id.the_name.
What is going wrong?

This is not how android works. It seems you miss the very basics.
R.id.button is just an Int, the id of a view. To get the view you can do findViewById(id) for example.
So then in your case maybe:
val button: Button = findViewById(R.id.button)
button.setOnClickListener {
//on click
}

You should implement view binding to your projects.
https://developer.android.com/topic/libraries/view-binding
So instead of val btn = findViewById<Button>(R.id.yourbuttonId) you can val btn = binding.yourbuttonId

Related

How to reference a button from different view in Fragment. Kotlin

I want to achieve a simple task. I have an invisible button in one layout and a have a Fragment. When this function is executed I want the button in the other layout to become visible. However this layout with the button is not in the Fragment layout, which means I have to reference that button in the Fragment, but I don't know how to go about that.
This is what Fragment will look like to first time users. The images you see are in a recyclerview which inflates a layout. That layout has the invisible button.
Fragment class
//item subscribed
if (subscribeValueFromPref) {
subscribeAbstract.visibility = View.GONE
// abstractDownload.visibility = View.VISIBLE
} else {
subscribeAbstract.visibility = View.VISIBLE
// abstractDownload.visibility = View.VISIBLE
}
}
When this line of code executed the button in the fragment is gone and the button from the other layout becomes visible. As you can see I put two strokes in front of that code. Once the code has been executed the layout should look like this.
Summary
I want to reference a button in another layout from a fragment class.
Do you even need to communicate between fragments? Your example looks like a single fragment with a subscribe button, and a RecyclerView that contains items which have a button that can be displayed or hidden. You can just make that part of your Adapter's state, like this:
class MyAdapter : RecyclerView.Adapter<ViewHolder>() {
var showDownloadButtons = false
set(value) {
field = value
// call this to update the display (calls onBindViewHolder for items)
notifyDataSetChanged()
}
override fun onBindViewHolder(viewHolder: ViewHolder, position.Int) {
...
// when displaying an item, show or hide the download button as appropriate
viewHolder.downloadButton.visibility = if (showDownloadButtons) VISIBLE else INVISIBLE
}
}
Then in your fragment, when you work out your UI state based on that subscription value, you can just handle your main button and tell the adapter what to display:
if (subscribeValueFromPref) {
subscribeAbstract.visibility = View.GONE
adapter.showDownloadButtons = true
} else {
subscribeAbstract.visibility = View.VISIBLE
adapter.showDownloadButtons = false
}

How to clear focus of BasicTextField upon clicking somewhere else in Compose Multiplatform?

I have a BasicTextField in Jetbrains Compose Multiplatform for desktop. When I click on it, the TextField gets focus and becomes editable. However, when I click somewhere else in my application, the focus is not lost and the field is still editable as if I just clicked on it.
I know this behavior is normal and intended. Nonetheless, I want to the TextField to become unfocused when the user clicks somewhere else, regardless of it being a clickable or non-clickable composable.
How do I achieve this?
This is one way I've done it in the past.
val keyboardController = LocalSoftwareKeyboardController.current
val focusManager = LocalFocusManager.current
val interactionSource = remember { MutableInteractionSource() }
Then I made my parent layout clickable.
Box(modifier = Modifier
.clickable(
interactionSource = interactionSource,
indication = null // this gets rid of the ripple effect
) {
keyboardController?.hide()
focusManager.clearFocus(true)
}

How Should I implement onbackpressed() in fragment kotlin [duplicate]

I am using The new Navigation Architecture Component in android and I am stuck in clearing the navigation stack after moving to a new fragment.
Example:
I am in the loginFragment and I want this fragment to be cleared from the stack when I navigate to the home fragment so that the user will not be returned back to the loginFragment when he presses the back button.
I am using a simple NavHostFragment.findNavController(Fragment).navigate(R.id.homeFragment) to navigate.
Current Code :
mAuth.signInWithCredential(credential)
.addOnCompleteListener(getActivity(), new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
NavHostFragment.findNavController(LoginFragment.this).navigate(R.id.homeFragment);
} else {
Log.w(TAG, "signInWithCredential:failure", task.getException());
}
}
});
I tried using the NavOptions in the navigate(), but the back button is still sending me back to the loginFragment
NavOptions.Builder navBuilder = new NavOptions.Builder();
NavOptions navOptions = navBuilder.setPopUpTo(R.id.homeFragment, false).build();
NavHostFragment.findNavController(LoginFragment.this).navigate(R.id.homeFragment, null, navOptions);
First, add attributes app:popUpTo='your_nav_graph_id' and app:popUpToInclusive="true" to the action tag.
<fragment
android:id="#+id/signInFragment"
android:name="com.glee.incog2.android.fragment.SignInFragment"
android:label="fragment_sign_in"
tools:layout="#layout/fragment_sign_in" >
<action
android:id="#+id/action_signInFragment_to_usersFragment"
app:destination="#id/usersFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/main_nav_graph"
app:popUpToInclusive="true" />
</fragment>
Second, navigate to the destination, using above action as parameter.
findNavController(fragment).navigate(
SignInFragmentDirections.actionSignInFragmentToUserNameFragment())
See the docs for more information.
NOTE: If you navigate using method navigate(#IdRes int resId), you won't get the desired result. Hence, I used method navigate(#NonNull NavDirections directions).
I think your question specifically pertains on how to use the Pop Behavior / Pop To / app:popUpTo (in xml)
In documentation,
Pop up to a given destination before navigating. This pops all non-matching destinations from the back stack until this destination is found.
Example (Simple Job hunting app)
my start_screen_nav graph is like this:
startScreenFragment (start) -> loginFragment -> EmployerMainFragment
-> loginFragment -> JobSeekerMainFragment
if I want to navigate to EmployerMainFragment and pop all including startScreenFragment then the code will be:
<action
android:id="#+id/action_loginFragment_to_employerMainFragment"
app:destination="#id/employerMainFragment"
app:popUpTo="#+id/startScreenFragment"
app:popUpToInclusive="true" />
if I want to navigate to EmployerMainFragment and pop all excluding startScreenFragment then the code will be:
<action
android:id="#+id/action_loginFragment_to_employerMainFragment"
app:destination="#id/employerMainFragment"
app:popUpTo="#+id/startScreenFragment"/>
if I want to navigate to EmployerMainFragment and pop loginFragment but not startScreenFragment then the code will be:
<action
android:id="#+id/action_loginFragment_to_employerMainFragment"
app:destination="#id/employerMainFragment"
app:popUpTo="#+id/loginFragment"
app:popUpToInclusive="true"/>
OR
<action
android:id="#+id/action_loginFragment_to_employerMainFragment"
app:destination="#id/employerMainFragment"
app:popUpTo="#+id/startScreenFragment"/>
In my case i needed to remove everything in the back Stack before i open a new fragment so i used this code
navController.popBackStack(R.id.fragment_apps, true);
navController.navigate(R.id.fragment_company);
the first line removes the back Stack till it reaches the fragment specified in my case it's the home fragment so it's removes all the back stack completely , and when the user clicks back in the fragment_company he closes the app.
Going to add another answer here as none of the above worked for me ... we have multiple nav graphs.
findNavController().navigate(R.id.dashboard_graph,null,NavOptions.Builder().setPopUpTo(findNavController().graph.startDestination, true).build())
This was the only way that I could successfully clear the full back stack. Google really need to make this simpler.
NOTE: Clear task is deprecated, official description is
This method is deprecated. Use setPopUpTo(int, boolean) with the id of the NavController's graph and set inclusive to true.
Old Answer
If you don't wanna go through all that fuzz in code, you can simply check Clear Task in Launch Options in properties of the action.
Edit: As of Android Studio 3.2 Beta 5, Clear Task is no longer visible in Launch Options window, but you can still use it in navigation's XML code, in action tag, by adding
app:clearTask="true"
NavController navController
=Navigation.findNavController(requireActivity(),
R.id.nav_host_fragment);// initialize navcontroller
if (navController.getCurrentDestination().getId() ==
R.id.my_current_frag) //for avoid crash
{
NavDirections action =
DailyInfoSelectorFragmentDirections.actionGoToDestionationFragment();
//for clear current fragment from stack
NavOptions options = new
NavOptions.Builder().setPopUpTo(R.id.my_current_frag, true).build();
navController.navigate(action, options);
}
I finally figure it out thanks to How to disable UP in Navigation for some fragment with the new Navigation Architecture Component?
I had to specify .setClearTask(true) as a NavOption.
mAuth.signInWithCredential(credential)
.addOnCompleteListener(getActivity(), new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "signInWithCredential:success");
NavOptions.Builder navBuilder = new NavOptions.Builder();
NavOptions navOptions = navBuilder.setClearTask(true).build();
NavHostFragment.findNavController(LoginFragment.this).navigate(R.id.homeFragment,null,navOptions);
} else {
Log.w(TAG, "signInWithCredential:failure", task.getException());
}
}
});
use this code
navController.navigateUp();
then call new Fragment
android version 4.1.2
Here is how I am getting it done.
//here the R.id refer to the fragment one wants to pop back once pressed back from the newly navigated fragment
val navOption = NavOptions.Builder().setPopUpTo(R.id.startScorecardFragment, false).build()
//now how to navigate to new fragment
Navigation.findNavController(this, R.id.my_nav_host_fragment)
.navigate(R.id.instoredBestPractice, null, navOption)
For
// Navigation library
def nav_version = "2.3.5"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
This solution work for me
findNavController().popBackStack(R.id.<Current Fragment Id In NavGraph>, true)
findNavController().navigate(R.id.< Your Destination Fragment in NavGraph>)
In my case where I used Navigation component with NavigationView (menu drawer):
1.
mNavController.popBackStack(R.id.ID_OF_FRAGMENT_ROOT_TO_POP, true)
mNavController.navigate(
R.id.DESTINATION_ID,
null,
NavOptions.Builder()
.setPopUpTo(R.id.POP_TO_DESTINATION_ID, true)
.build()
)
I wanted to clear the stack after clicking on logout on side menu drawer!
Hope that helped someone!
You can override the back pressed of the base activity like this :
override fun onBackPressed() {
val navigationController = nav_host_fragment.findNavController()
if (navigationController.currentDestination?.id == R.id.firstFragment) {
finish()
} else if (navigationController.currentDestination?.id == R.id.secondFragment) {
// do nothing
} else {
super.onBackPressed()
}
}
Non of the solutions above works for me.
After spending hours on it, here is my solution:
Note: I have multiple nav_graphs and switching between fragments in different nav_graphs.
Define your action as below in xml:
<action
android:id="#id/your_action_id"
app:destination="#id/the_fragment_id_you_want_to_navigate_to"
app:popUpTo="#id/nav_graph_which_contains_destination_fragment"
app:popUpToInclusive="true" />
Navigate using action above from your Java/Kotlin code:
findNavController(R.id.your_nav_name)?.apply {
navigate(R.id.your_action_id)
backQueue.clear()
}
For Jetpack Compose ❤️
navHostController.navigate(Routes.HOME) {
this.popUpTo(Routes.ONBOARDING) {
this.inclusive = true
}
}
I struggled for a while to prevent the back button from going back to my start fragment, which in my case was an intro message that should only appear once.
The easy solution was to create a global action pointing to the destination that the user should stay on. You have to set app:popUpTo="..." correctly - set it to the destination you want to get popped off. In my case it was my intro message. Also set app:popUpToInclusive="true"
In my case, I am using 2 different activities that have their own respective navigation graphs. My first activity is the host for "nav_graph" and has fragments that deal with authentification and the second is the host for "nav_graph_home". Here you can see the settings I have done for nav_graph.
nav_graph example
Then back in my code for the login fragment, I have this written :
findNavController().navigate(R.id.action_logInFragment_to_nav_graph_home)
After the user logs in and they hit the back button the app will close. Remember to set the pop behavior so it pops till your current navigation graph that contains your login fragment without including it.
Edit:
After this, the up button still appears in the top bar. To avoid this behavior we need to tell the first activity which fragments are considered top level. To do this simply add in the params list of the "setupActionBarWithNavController()" method in addition to the nav host fragment an App bar configuration that contains a set of the home fragment of your first navigation graph and your second. Your code should look something like this:
class MainActivity : AppCompatActivity(R.layout.activity_main) {
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Retrieve NavController from the NavHostFragment
val navHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
navController = navHostFragment.navController
// Set up the action bar for use with the NavController
setupActionBarWithNavController(navController, AppBarConfiguration(setOf(R.id.logInFragment,R.id.homeFragment)))
}
/**
* Handle navigation when the user chooses Up from the action bar.
*/
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp() || super.onSupportNavigateUp()
}
}
This is my first ever contribution, hope this helps.
For androidx.compose version 1.2.0+
I had a few issues with lower versions but 1.2 plus (beta at the time of writing this), works perfectly.
Better syntax for the navGraph in Compose:
navController.navigate(item.name) {
navController.graph.startDestinationRoute?.let { route ->
// use saveState = false to NOT save the state for the popped route
popUpTo(route) { saveState = true }
}
launchSingleTop = true
restoreState = true
}
I am using button to navigate to other fragments so on each button click I am doing this.
val navOptions = NavOptions.Builder().setLaunchSingleTop(true).setPopUpTo(R.id.homeFragment, false).build()
findNavController(R.id.mainNavHostFragment).navigate(R.id.destination, null, navOptions)
You can do as simple as:
getFragmentManager().popBackStack();
If you want to check the count you can do as:
getFragmentManager().getBackStackEntryCount()

How to change the fonts of all items on recyclerview runtime

I wanted to change font family of items on a recycler view every time I click a button.
So I coded like below.
rbAritaBuri = view.findViewById(R.id.rb_aritaBuri)
rbCafe24 = view.findViewById(R.id.rb_cafe24SurroundAir)
rbAritaBuri.setOnClickListener {
rv_work_preview.tv_work_content.typeface = Typeface.createFromAsset(requireActivity().assets, "fonts/arita_buri.otf")
}
rbCafe24.setOnClickListener {
rv_work_preview.tv_work_content.typeface = Typeface.createFromAsset(requireActivity().assets, "fonts/cafe24_surround_air.ttf")
}
But it changes only the font family of the first item of the recycler view.
Is there a way to change fonts of them all together runtime? And please tell me why the code I wrote doesn't work right.
Thank you.
If I were in your position, I would:
Put your font changing calls inside of onBindViewHolder(). If you have to, you could put a bool in there like buttonClicked and link its value to your buttons.
Come up with a good way to force a call to onBindViewHolder(). Sometimes notifyDataSetChanged() is enough. But in some cases, you might have to remove the adapter by setting it to null and then reset the adapter to its original value.
Place that logic from step 2 inside of your buttons' onClick()s.
Edit:
What I mean is, create a var inside the class with the most exterior scope, so outside of oncreate().
var textChoice=""
Now use your buttons to change that var.
rbAritaBuri.setOnClickListener {
textChoice="fonts/arita_buri.otf"
}
Now inside your onBindViewHolder(), make the font switch.
when (fontChoice){
"fonts/arita_buri.otf"->{ rv_work_preview.tv_work_content.typeface = Typeface.createFromAsset(requireActivity().assets, "fonts/arita_buri.otf")}
//and so on and so forth for all of your fonts
Now when you want to show the change, call notifyDatasetChanged(). I think maybe the best place to do that would be inside of your buttons. So maybe you'd actually have:
rbAritaBuri.setOnClickListener {
textChoice="fonts/arita_buri.otf"
<The name of your recyclerview adapter>.notifyDatasetChanged()
}
Here is how I solved it, thanks to D. Kupra:
class SampleWorkAdapter(private val context: Context) :
RecyclerView.Adapter<SampleWorkAdapter.ViewHolder>() {
var selectedFont = EditTextActivity.HAMBAK_SNOW
First, I assigned the default font Hambak_snow to selectedFont, type String.
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
...
fun changeFont(font: String) {
CustomFontHelper.setCustomFont(content, font, itemView.context)
} ...
}
Then I wrote a function to be called on onBindViewHolder to change font-family of textview, using custom typeface. https://stackoverflow.com/a/16648457/15096801 This post helped a lot.
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
...
viewHolder.changeFont(selectedFont)
...
}
Now, replaceFont will be called when the variable selectedFont get changed and adapter.notifyDatasetChanged() is called on an activity, like this:
rbMapoFlowerIsland.setOnClickListener {
sampleWorkAdapter.selectedFont = EditTextActivity.MAPO_FLOWER
sampleWorkAdapter.notifyDataSetChanged()
}

Android scroll to position in Recylerview not working

I want to scroll the RecyclerView to a position, and I use LinearLayoutManager.scrollToPositionWithOffset() to scroll.
But it does not work. Could any one help me?
RecyclerView rv = findViewById(R.id.recycler_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
rv.setLayoutLayoutManager(layoutManager);
layoutManager.smoothScrollToPosition(position);
Scrolling RecyclerView to certain item position can be achieved with LayoutManager which is used with that particular RecyclerView. Plz refer to above piece of code
If you don't have a lot of items in your list, you can use LinearSmoothScroller
val smoothScroller = object : LinearSmoothScroller(activity) {
override fun getVerticalSnapPreference(): Int {
return SNAP_TO_START
}
}
and then when you want to scroll:
smoothScroller.targetPosition = position // position on which item you want to scroll recycler view
binding.cardsRecycler.layoutManager?.startSmoothScroll(smoothScroller)