findViewById dynamic cast in func? - java

I would like to make a function that automaticly cast my findViewById-View-from reference.
Can i achive this somehow?
private View getReferenceForView(View view, int resId)
{
view = (view.getClass())findViewById(resId);
return view;
}
view.getClass() is not accepted there, but i would like to achive something like this in call:
myView = getReferenceForView(myView, R.drawable.someresid);
So i can get reference for my views without the annyoing casting part.

The only way to use the approach you want is to use reflections instanceof. But this leads in a huge if-else block for every available View in Android.
You would have something like the following:
private View getReferenceForView(View view, int resId)
{
if(view instanceof TextView)
view = view.findViewById(resId);
else if(view instanceof EditText)
// and so on..
return view;
}
I don't know your exact approach but I think this isn't worth the effort.

Related

Android. Recycler view with mutiple view types

I have recyclerView with 2 view types (item and FOOTER). I need to show my footer on the bottom of screen even if no items or items size is 1. Is it possible to implement it? Now my footer is showing after last item, but I need show footer always on the bottom.
You need a data structure that allows you to do so, and then you need the view holders for supporting it, once that is done handling the conditional flows on the adapter and should be good to go.
Usually, in Kotlin we use a sealed class which allows very good type control
sealed class AdapterRow {
data class RegularItem(/*your values*/) : AdapterRow()
data class FooterItem(/*your values*/) : AdapterRow()
//if the footer is always the same maybe object FooterItem : AdapterRow() is more suitable
}
It's a nice "trick" to have the sealed descendants inside so that way the sealed parent makes a domain space name, then you call them like this AdapterRow.RegularItem(...). If you don't like that, the sealed class has one constraint, descendants must be on the same file.
Then you need a view holder for supporting each type (view holder and the view in the layout if needed). In this case we are gonna use an abstract class to take advantage of the polymorphism and abstract methods
//you could use binding here and then the implementation define the binding type
abstract class BaseViewHolder(view: View) : RecyclerView.ViewHolder(view) {
abstract fun bind(row: AdapterRow)
}
And then the children:
class RegularItemViewHolder(view: View) : BaseViewHolder(view) {
override fun bind(row: AdapterRow) {
//validate the data you are receiving is correct
if (row !is AdapterRow.RegularItem) return
//do your data bindings, view findings or whatever
}
}
With the above you can deduce the other view holder, now the adapter methods
getItemViewType(position: Int) {
when(getItem(position)) {
is AdapterRow.RegularItem -> R.layout.REGULAR_ITEM_LAYOUT
is AdapterRow. FooterItem -> R.layout.FOOTER_ITEM_LAYOUT
}
}
onCreateViewHolder(...) {
return when (viewType) {
R.layout.REGULAR_ITEM_LAYOUT -> {
RegularItemViewHolder(...)
}
R.layout.FOOTER_ITEM_LAYOUT {
//same but for footer
}
else -> throw RuntimeException("Unsupported view Holder)
}
}
onBindViewHolder(...) {
holder.bind(getItem(position))
}
The data construction part is the last thing, in somewhere else, your fragment, the view model, etc, you have to create the data structure as you need. By example:
fun makeRows(texts: List<String>): List<AdapterRow> {
val rows = mutableListOf<AdapterRow>()
texts.forEach { text ->
//do some mapping from what ever is your source in this case strings
AdapterRow.RegularItem(...text)
}
//so it doesn't matter if the source of data is empty at the end you always add the footer
rows.add(AdapterRow.FooterItem)
}
And then is just passing the data to the adapter, if you are using ListAdapter
val rows = someClass.makeRows(....)
yourAdapter.submitList(rows)

return findViewById() in viewbinding

I am trying to optimize old code. And I am trying to replace findviewbyid with viewbinding.
But how do I return viewbinding id instead of findviewbyid?
private TextView getTextView(int id){
return (TextView) findViewById(id);
}
This is the old code. But I want to apply viewbinding. I want it to work something like this. As I have no idea how to do it.
private TextView getTextView(int id){
return sampleViewBinding(id);
}
How can I achieve this?
The whole point of View Binding is to avoid findViewById() calls. It does it for you automatically. What you are trying to do is treating View Binding like findViewById(). Whenever you need to access any view, all you have to do it call the generated binding class with your id in the camel-case. for e.g main_layout.xml is gonna have a class generated by the name MainLayoutBinding so you are going to access all the view inside your layout by calling the MainLayoutBinding's instance and the id you want to access.
If your layout file name is fragment_dashboard.xml and has within a textview with an Id userNameTvId, then you normally do this without using data binding:
val view = inflater.inflate(R.layout.fragment_dashboard, container, false)
val textview = view.findViewById(R.id.userNameTvId)
but with viewBinding it is done by chaining. this textview is acceptable through the binding object. The above will be rewritten like this using viewBinding:
val binding = FragmentDashboardBinding.inflate(inflater)
binding.userNameTvId
// to pass a value you can just do
binding.userNameTvId = "SomeOne"
val view = binding.root

Find all views by id

I know it's not the best practice, but I have a few layouts (only one is visible at a time)
with the same view_id in all of them.
Is there any way method to get all views with this id?
I can iterate all layouts and layout.findViewById() but wanted to ask if there is something similar to a CSS findViewsById() views instead of view
Activity::findViewById() returns just the first one.
No, there is no method like that. If some "dirty hack" exists it will be unstable.
"Best practice" here will be iterate layouts and store references on these views in array.
You can make your own. In that case you have to use setContentView(View) instead. So your findViewsById() would be,
public View[] findViewsById(int id) {
View[] views = new View[contentViews.length];
for(int i=0; i<contentViews.length; i++) {
views[i] = contentViews[i].findViewById(id);
}
return views;
}
Your activity should look like the following,
private View[] contentViews;
#Override
public void onCreate(Bundle si) {
super.onCreate(si);
populateContentViews();
setContentView(contentViews[x]);
...
}
Of course you can.
Just do it in recursion:
For each view check its ID, and do it for its children.
That's it.
After all, that's almost what findViewById does. It searches in the view hierarchy like a recursion, till it finds it.

How to generically retrieve all children of a certain type in a root layout?

What I mean by my question (if I stated it ambiguously, as I couldn't find an answer to my question) is to take a root layout, get all children of that layout, and perform a callback on any that are an instanceof the specified type.
Now, I can do it in a fixed way, easily by doing something like...
RelativeLayout root = (RelativeLayout) findViewById(R.id.root_layout);
for(int i = 0; i <= root.getChildCount(); i++){
View v = root.getChildAt(i);
if(v instanceof CustomLayout){
// Do Callback on view.
}
}
Thing is, I want to make it more generic. I should be able to use any layout, and check to see if it is an instance of any layout. In particular, I want it generic enough to be used with anything (if this is even possible). Of course I don't mind stopping at just settling for layouts.
I want to build a collection of these children and return them, if possible of the same type. I haven't done Java in a long while so I'm very rusty, but I was thinking of using reflection to accomplish this. Is this at all possible?
If I pass the class of the type I want, is it possible?
Edit:
I didn't see dtann's answer before, must have missed it, but I did it on my own and it looks very similar to his. My implementation went something along the lines of this
public static abstract class CallbackOnRootChildren<T> {
#SuppressWarnings("unchecked")
public void callOnChildren(Class<T> clazz, ViewGroup root) {
for(int i = 0; i < root.getChildCount(); i++){
View v = root.getChildAt(i);
if(v instanceof ViewGroup){
callOnChildren(clazz, (ViewGroup) v);
}
if(clazz.isAssignableFrom(v.getClass())){
// The check to see if it is assignable ensures it's type safe.
onChild((T) v);
}
}
}
public abstract void onChild(T child);
}
Difference is that mine relies on callbacks and whatnot, but overall same concept.
Try the following code:
public <T> List<T> getViewsByClass(View rootView, Class<T> targetClass) {
List<T> items = new ArrayList<>();
getViewsByClassRecursive(items,rootView,targetClass);
return items;
}
private void getViewsByClassRecursive(List items, View view, Class clazz) {
if (view.getClass().equals(clazz)) {
Log.d("TAG","Found " + view.getClass().getSimpleName());
items.add(view);
}
if (view instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup)view;
if (viewGroup.getChildCount() > 0) {
for (int i = 0; i < viewGroup.getChildCount(); i++) {
getViewsByClassRecursive(items, viewGroup.getChildAt(i), clazz);
}
}
}
}
Call getViewsByClass and pass in the root layout and target class. You should receive back a list of all the views that are instance of the target class. This would include the root layout itself if its also an instance of the target class. This method will search the entire view tree of the root layout.
There is no generic way for this. If anyone does this will simply do the similarly.
Views in viewgroup are kept in field like (source-code):
// Child views of this ViewGroup
private View[] mChildren;
// Number of valid children in the mChildren array, the rest should be null or not
// considered as children
private int mChildrenCount;
See documentation

How to get current content view in Android programming?

I know that I can set the content of the view in an Android app by saying setContentView(int). Is there a function I can use to know what the current content view is? I don't know if that makes any sense, but what I'm looking for is a function called, say, getContentView that returns an int.
Ideally, it would look like this:
setContentView(R.layout.main); // sets the content view to main.xml
int contentView = getContentView(); // does this function exist?
How would I do that?
Citing Any easy, generic way in Android to get the root View of a layout?
This answer and comments give one method: [Get root view from current activity
findViewById(android.R.id.content)
Given any view in your hierarchy you can also call:
view.getRootView()
to obtain the root view of that hierarchy.
The "decor view" can also be obtained via getWindow().getDecorView(). This is the root of the view hierarchy and the point where it attaches to the window, but I'm not sure you want to be messing with it directly.
You can do making a setter and getter of current view by id only
private int currentViewId = -1;
public void setCurrentViewById(int id)
{
setContentView(id);
currentViewId = id;
}
public int getCurrentViewById()
{
return currentViewId;
}
And then in
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setCurrentViewById(R.layout.main_layout);
}
Hope this helps.
In an Activity, you can do
View rootView = null;
View currentFocus = getWindow().getCurrentFocus();
if (currentFocus != null)
rootView = currentFocus.getRootView();
As described above, there is also
View decorView = getWindow().getDecorView();
as well as
View decorView = getWindow().peekDecorView();
The difference between the latter two is that peekDecorView() may return null if the decor view has not been created yet, whereas getDecorView() will create a new decor view if none exists (yet). The first example may also return null if no view currently has focus.
I haven't tried out whether the root view and the decor view are the same instance. Based on the documentation, though, I would assume they are, and it could be easily verified with a few lines of code.
if you have two content views then you can put a tag inside the relative layout of each one. and then get the view by tag name. if tag name is the one desire then blablabla. Hope this help for whoever is searching for a solution.

Categories