[Android development] Handle set text for Android app

Thế Dũng
3 min readJun 15, 2021

Basically, you don’t meet this problem in a real-normal application. But for some reasons, you’ll need to update your string resource without release a brand new app version on Google play:

  • You need to modify your text resource
  • You need to support new language
  • You need to custom the way Android set text to your apps.

These above reasons may not match what you thought but, I have to brainstorm to give you those three (may be not really smart) reasons.

So, enough talking. Let’s move on the idea.

I have read not too much but enough for me to recognize what we should do to archive our goals.

  • First: Override the get text function by override the get resource function by override wrapper context by override get delegate function from an Activity. This thing is used for set text programmatically. (You should think about tvSomeThing.text = getText(R.string.something). That what exactly what I hope your thinking will work).
  • Second: Not override but re-set text into your layout xml by intercepting the process of InflaterLayout. (So, again, I need you to think about android:text=”@string/something”. We gonna reset this thing. And some others too)

Part 1: Deal with set text programmatically

First thing first, override. Always override everything you meet.

override fun getDelegate()

If you come from the pass, you don’t need to do this step. Because Android need this from appcompat:1.2.0 and now, when I wrote this article, I use appcompat:1.3.0.

But you don’t have the BaseContextWrappingDelegate.kt. Ok, that’s fine.

What you should notice in this file is:

private fun wrap(context: Context): Context {        
return TraverseStringContextWrapper(context)
}

Sorry because the above code don’t have color. This will allow us to use our custom ContextWrapper instead of the original one. And one important note is you should keep the package as it is:

package androidx.appcompat.app

You need to be apart of androidx app compat package. Because you use their internal things. So, remember to keep this line of code.

Now, continue!

Let’s ignore the getSystemService() function. It’s not in this part. I’ll talk about that later.

The key point here is we override the getResources function with our resources. And inside our custom resources, we override the getText(id: Int) which I had talked before, override everything.

We should also know that what is the id here? That’s a hard question. We should keep it the same as id in strings.xml file. So we have the id for every text, and if our repository don’t contain that text id, the we can easily navigate to the app resource.

Here I use a repository. It will not appear in this article. But what it does is Go and get your custom strings. If it can not find the string, go with the original resources.

Soooooooo now, we’ve done!

We’ve done part one. Now if you go and create some TextView and set text for them, you can see your custom resources work.

Part 2: LayoutInflater

Now, rollback to TraverseStringContextWrapper and see getSystemService()

override fun getSystemService(name: String): Any? {        
if (Context.LAYOUT_INFLATER_SERVICE == name) {
return TraverseStringLayoutInflater( this,
LayoutInflater.from(base))
}
return super.getSystemService(name)
}

This is use for intercepting layout inflater work.

Instead of using default LayoutInflater, we create a custom of it. Nice!

Really long code snippet. I hate that. Agrrr, but it has color. Nice!

I will provide you the source code first, then go through all of them later.

Here is the TraverseLayoutInterceptorCompositor.kt file.

And for toolbarInterceptor() + textViewInterceptor() you can find it here

Define WrapperFactory2:

Define WrapperFactory:

Explain:

TraverseLayoutInterceptorCompositor is the one that will find view and match them with the corresponding interceptor. If the view is TextView, then it will find the textViewInterceptor to process the inflating layout.

And what textViewInterceptor() does is finding the attribute that set text for the view, and then reset it with our custom repository, which will return our resource string.

I also have override WrapperFactory and WrapperFactory2. These 2 class is served for our intercept create view. Pause the view creating process, then put the interceptor into it, then use this interceptor to reset the resource if needed. Then continue drawing the view.

But why it is 2 factory? Android maker. I don’t know, but Factory is extended by Factory.

That’s it!

If you have any question about this, please put a comment and I will answer it with all my knowledge.

The source code here

Thanks for reading!

REFRENCE:

--

--