Kotlin App wide change TextView Color Programatically - kotlin

How does one change the TextView default color throughout the entire app programmatically?
Not individual textView but every TextView in all activity layouts
Either getting the current theme and setting the default TextView Color, if <TextView has textColor="" in the xml, then leave it alone.
But if <TextView does not reference a textColor it's getting the default (for the theme)
How do I set this based on code/logic
Thanks in advance

Here is the object's code:
object Example {
fun getColor(c: Context) : Int {
if (someCondition) {
return (c.getColor(R.color.colorAccent))
} else {
return (c.getColor(R.color.colorPrimaryDark))
}
}
}
you call the get method every time you need to alter the view (you can store the TextViews inside a list)
Alternatively you can use the meterial theme feature that is explained here: https://proandroiddev.com/dark-mode-on-android-app-with-kotlin-dc759fc5f0e1

Related

Adapter with Picasso

I'm trying to use Picasso to get images from Url and put inside an Adapter. I have to send the image as a parameter to the funcion imageSlider inside the my SliderViewHolder. Can someone explain me how?
Picasso get:
Picasso.get()
.load(sliderImages[position])
.transform( RoundedTransformation(30, 0))
.placeholder(context.resources.getDrawable(R.drawable.ic_launcher_foreground))//it will show placeholder image when url is not valid.
.networkPolicy(NetworkPolicy.OFFLINE) //for caching the image url in case phone is offline
.into(holder.imageSlider(?))
Viewholder:
class SliderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val imageView: RoundedImageView = itemView.findViewById(R.id.image_slide)
fun imageSlider(sliderItem: SliderItem) {
imageView.setImageResource(sliderItem.image)
}
}
The Function
fun imageSlider(sliderItem: SliderItem) {
imageView.setImageResource(sliderItem.image)
}
is not necessary, because you declare the imageView with a public visibility.
In the Adapter of your RecyclerView, there is a Method called
override fun onBindViewHolder(holder: SliderViewHolder, position: Int)
This one will get invoked, when the Adapter wants to bind your Item Layout (ViewHolder) with a "backing data item" of the List...
Then you could simply do this:
Picasso.get()
.load(sliderImages[position])
.transform( RoundedTransformation(30, 0))
.placeholder(context.resources.getDrawable(R.drawable.ic_launcher_foreground))//it will show placeholder image when url is not valid.
.networkPolicy(NetworkPolicy.OFFLINE) //for caching the image url in case phone is offline
.into(holder.imageView)
Which will make Picasso, load the Image into the ImageView of your ViewHolder (which actually simply holds the Item Layout) for the current Item in the List.
The correct implementation would actually be, that you give your Adapter a List of Image Objects (for example, containing Properties like Image Name and a Path/URL, that should get "rendered") and when the Adapter is invoking "onBindViewHolder" with the corresponding Position, it is your Job to implement the loading of the image from the given path for the given position.

Custom CollapsingTopAppBar Jetpack Compose

The essence of the problem is that I want to write my own version of the AppBar that would include content as another Compose function. After looking at the source code of the current CollapsingTopAppBar implementation, I saw the following lines:
#Composable
private fun TwoRowsTopAppBar(
...
scrollBehavior: TopAppBarScrollBehavior?
) {
...
val pinnedHeightPx: Float = 64.dp
val maxHeightPx: Float = 152.dp
LocalDensity.current.run {
pinnedHeightPx = pinnedHeight.toPx()
maxHeightPx = maxHeight.toPx()
}
// Sets the app bar's height offset limit to hide just the bottom title area and keep top title
// visible when collapsed.
SideEffect {
if (scrollBehavior?.state?.heightOffsetLimit != pinnedHeightPx - maxHeightPx) {
scrollBehavior?.state?.heightOffsetLimit = pinnedHeightPx - maxHeightPx
}
}
...
Surface(...) {
Column {
TopAppBarLayout(
...
heightPx = pinnedHeightPx
...
)
TopAppBarLayout(
...
heightPx = maxHeightPx - pinnedHeightPx + (scrollBehavior?.state?.heightOffset
?: 0f),
...
)
}
}
}
As I understand it, scrollBehavior is used to handle the collapse and expansion behavior. In the current implementation, just constant values are put in heightOffsetLimit. And since I need my appbar implementation to be able to contain content of any size, I need to somehow know the size of this content in advance and put this value in heightOffsetLimit.
I have already written the code for my AppBar, so that it also contains content. But since I can't pass the height value of the content to scrollBehavior, the AppBar doesn't collapse to the end.
you need to calculate the height that the appbar will have before drawing it into the screen. I have followed this issue and solved my problem with the last solution. hope it helps:
Get height of element Jetpack Compose
use the content you can put (ex. an image or a huge text) as the MainContent
use your appbar as the DependentContent and use the size given in lambda to give the height to your appbar
finally set placeMainContent false as I believe you don't need to draw the image (or any other composable) directly in a box
and you will good to go

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

NavigationLink button focusable override issue

I face an issue which stucks for days. I am createing a tvos application which reqiures a custome navigationlink(button), when I move the focus to the navigation item, it should scale a little bit, and also I need to change the parent's view backgound. It is pretty simple, but it seems that the focusabe override the my custome button Style. The test shows that the background image was changed but without any scale effect when the navigationbutton get focused. Any suggestion?
NavigationLink(destination: Text("myview"))
{Text("Test")
}
.buttonStyle(myButtonStyle())
.Focusable(true){(focus) in
//the code to change the background image
//myButtonStyle definition
struct MyButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
return AppButton(configuration: configuration)
}
}
struct AppButton: View {
#Environment(\.isFocused) var focused: Bool
let configuration: ButtonStyle.Configuration
var body: some View {
configuration.label
.scaleEffect(focused ? 1.1 : 1.0)
.focusable(true)
}
}
The line to change the background image is always called when the item get focused as my expected, but the scale effect is gone. If I remove the following line of codes, the scale effect is back:
// .Focusable(true){(focus) in
//the code to change the background image
// }
It looks like that this line of code override my custome style of navigation button, any ideas? Appreciate any help!
Ah, finally I found the tricky, though there is very little document about this. When Focusable was introduced, it should not be in your code to change focus engine, which will cause the navigationlink tap message uncaptured, then your navigationlink for another view will not work.
Use .onChange() function to deal with any focus change event, not use Focusable.

talkback not focusing by default on any view on start of inner fragment

I am using accessibility talkback functionality and I am facing one problem I have one bottom navigation in the parent activity and from the setting tab I am opening another fragment(inner fragment) using .add but the inner fragment view not getting focus by default
I also tried with . replace but it's not focusing by default on fragment creation.
open fragment code
val details = DetailsFragment.newInstance();
getSupportFragmentManager().setupForAccessibility()
getSupportFragmentManager().beginTransaction().add(android.R.id.content, details).commit()
and I used this extension function to not get focus on the previous fragment from this source
fun FragmentManager.setupForAccessibility() {
addOnBackStackChangedListener {
val lastFragmentWithView = fragments.lastOrNull { it.view != null }
for (fragment in fragments) {
if (fragment == lastFragmentWithView) {
fragment.view?.importantForAccessibility =
View.IMPORTANT_FOR_ACCESSIBILITY_YES
} else {
fragment.view?.importantForAccessibility =
View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
}
}
}
}
in normal I show that at the start of the first fragment it's focusing top first Textview and speaking automatic but in the inner fragment it's not focusing by default so what should I do to get focus by default on the first view by default
I already try
android:focusable="true"
android:focusableInTouchMode="true"
and request focus but it's not working
Please suggest me any help would be highly appriciated
I've had the best luck using
view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
on the view you want to focus, or maybe
Handler().postDelayed({
view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
}, someDelayMillis)
to let the system do whatever it does, and then jump in after a moment and force a focus change.
But this might be considered bad accessibility, if it's interfering with the usual navigation, and that might be why it's so hard to get focus working consistently! It might be better to just announce the new fragment (with something like view.announceForAccessibility("new fragment")) and let the user start navigating from the top. It depends on your app, it's your call
Also you probably want to use replace for your fragment instead of add, if you add a fragment on top of an old one, TalkBack can get stuck looking at the "invisible" views on the old fragment
this is my improved code that extends from #cactustictacs
//target to specific a view
binding.getRoot().postDelayed(new Runnable() {
#Override
public void run() {
binding.textView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
}
}, 300);
full: https://github.com/dotrinh-PM/AndroidTalkback