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
Related
I need to develop a tag picker, like the one Foursquare uses for tastes, and the one Flipboard uses for "finding new topics."
I came across this library, Foursquare-CollectionPicker at github.
However, it uses a linear layout, which can reduce the performance for numerous child views when scrolling.
Hence, I need to use a recyclerview. Can anyone suggest how to replicate this with a recyclerview? My problem is that for each row in the recyclerview, the column count could be different, based on the size/number of the child views in each row (tags, in this case).
Thank you.
You could use FlexboxLayoutManager in a recycler view. All you need to do is create the layout manager as below. Don't forget to add your own recyclerAdapter to the recycler view as well of course.
FlexboxLayoutManager layoutManager = new FlexboxLayoutManager(context);
layoutManager.setFlexWrap(FlexWrap.WRAP);
recyclerView.setLayoutManager(layoutManager);
https://android-developers.googleblog.com/2017/02/build-flexible-layouts-with.html
https://blog.devcenter.co/unboxing-the-flexboxlayout-a7cfd125f023
If all you need is a recycler view that changes the number of columns (using a standard Google provided GridLayoutManager in RecyclerView), you don't need any custom code at all.
(pseudo code)
Prerequisites
You use a RecylerView with a GridLayoutManager (import androidx.recyclerview.widget.GridLayoutManager)
Your Adapter has a Type (so different viewTypes can inflate different ViewHolders).
You can initialize your grid Layout like:
private lateinit var layoutManager: GridLayoutManager
private val adapter = YourAdapter()
Activity#onCreate(...) {
super.onCreate(savedInstance)
setContentView(...)
layoutManager = GridLayoutManager(this, 2) //defaults to two columns
yourRecyclerView.layoutmanager = layoutManager //set it
yourRecyclerView.adapter = adapter
//Here goes the magic
}
What is the Magic?
Something like this:
layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return when (adapter.getItemViewType(position)) {
adapter.viewTypeOneColumn -> 1
adapter.viewTypeTwoColumns -> 2
else -> -1
}
}
}
This obviously assumes you have a "view type" (of any sort)
it can be as simple as:
class YourAdapter : ... {
internal val viewTypeOneColumn = 0
internal val viewTypeTwoColumns = 1
...
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when(viewType) {
viewTypeOneColumn -> ViewHolderForOneColumn(...)
viewTypeTwoColumns -> ViewHolderForTwoColumns(...)
else -> throw IllegalArgumentException("You must supply a valid type for this adapter")
}
}
override fun getItemViewType(position: Int): Int {
return getItem(position).someThingThatClassifiesThem // this obviously depend on what you use to define what each item is...
}
}
And that's all you really need.
I have once created a sample that does this for displaying "ads" inside a RecyclerView: You can check it here (it was updated a while ago but the code still works the same).
https://github.com/Gryzor/GridToShowAds
You will need to write your own layout manager, take a look at these blogs:
http://wiresareobsolete.com/2014/09/building-a-recyclerview-layoutmanager-part-1/
http://simpleandstupid.com/2015/05/01/recyclerview-and-its-custom-layoutmanager/
Hello everyone.
I've posted a basic diagram of my Android project above. It's pretty sad but that's what 5 minutes in paint get you.
Anyway, I'll walk you through it. I have xml with a series of imageViews which have onClickListeners in my board.Java class. If one of the imageViews are clicked on, an instance of pawn.java is instantiated, I pass the context to the instantiated pawn object, then call its possibleMoves() method.
At the end of this method I generate a list of int's which happen to be the id's of the imageViews. the final portion of possibleMoves() is the following:
for (String s : chunks) {
String possibleSquare = "s" + s.substring(2, 4);
Toast.makeText(boardContext, possibleSquare, Toast.LENGTH_SHORT).show();
int id = boardContext.getResources().getIdentifier(possibleSquare, "id", boardContext.getPackageName());
System.out.println(id);
ImageView backgroundImg = (ImageView) findViewById(id);
backgroundImg.setBackgroundColor(Color.rgb(255, 255, 255));
}
return list;
The issue I'm having is that AndroidStudio says my findViewById(id) cannot be resolved. I've tried putting the context boardContext (the context I pass to my instantiated pawn object) in front of the findViewById, and I've tried using findViewById(R.id.id).
Suggestions?
findViewById(int id) are functions for View objects - here.. so you can not call it on nothing or any object. When you have reference to an Activity and you call findViewById(int id) it pulls the activity's contentView and calls it on it..
so your to your solution, Inflate the View containing your ImageView or get reference to your activity or if your context that you are passing is an Activity as context then you can cast your activity to the context and call your prefered method
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.
I have a Java Android application which I want to change to Scala. I have many fragments and I want to know what is the best way to do this in Scala.
This is my Java fragment class MyFragment:
public class MyFragment extends Fragment {
private WebView myWebView;
private TextView myTextView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View myView = inflater.inflate(R.layout.my_view, container, false);
myWebView = (WebView) myView.findViewById(R.id.my_webview);
myWebView.loadUrl("http://www.google.com/");
myTextView = (TextView) myView.findViewById(R.id.my_textview);
myTextView.setText("Google.com");
return myView;
}
}
I always have this base structure: some private UI elements which I instantiate in onCreateView, do some things and return the view (Not shown here: in other on* methods I also do some actions with the UI elements).
I found some articles which do a lazy val like described here: http://blog.andresteingress.com/2011/09/20/programming-android-with-scala/
But in my case, this does not work, because I have fragments and not activities. First I have to inflate the main View myView and then I can get the UI elements of it.
What is the best way to do this in Scala?
--- UPDATE ---
My Scala code looks like this at the moment:
class MyFragment extends Fragment {
private var myTextView: TextView = null
override def onCreateView(inflater: LayoutInflater,
container: ViewGroup, savedInstanceState: Bundle): View = {
val myView = inflater.inflate(R.layout.my_view, container, false)
val myWebView = myView.findViewById(R.id.my_webview).asInstanceOf[WebView]
myWebView.loadUrl("http://www.google.com/")
myTextView = myView.findViewById(R.id.my_textview).asInstanceOf[TextView]
myTextView.setText("Google.com")
myView
}
}
So, what can I improve here? myTextView is a private var because I have to access it several methods in this Fragment. It seems I can not do the stuff explained here: http://blog.andresteingress.com/2011/09/20/programming-android-with-scala/ with lazy val TypedActivity and the implicit conversion of OnClickListener, because I use fragments. So how can I get rid of boilerplate code with .asInstanceOf[T] and make it more Scala like?
Based on your updated code I can only make some suggestion to be more "scala-ish"
Use Option instead of null for your members
private var myWebView: Option[WebView] = None
private var myTextView: Option[TextView] = None
To avoid explicit casting of your views in the code, you need to move it elsewhere, but you cant' get rid of it, because the original android API doesn't give you any clue as to the runtime or compiletime type of the returned objects. To overcome this issue, the post you mentioned uses custom-made typed resources and a trait that handles the types from these.
case class TypedResource[T](id: Int)
object TR {
object id {
val my_webview = TypedResource[TextView](R.id.my_webview)
val my_textview = TypedResource[WebView](R.id.my_textview)
//... you must put here all your typed views referenced by id
}
}
trait TypedViewHolder {
def view: View
//the method explicitly casts to the needed resource type based on the argument
def findView[T](tr: TypedResource[T]): T = view.findViewById(tr.id).asInstanceOf[T]
}
object TypedResource {
//this will implicitly convert your views to a corresponding TypedViewHolder
//this lets you avoid explicit type cast to get your view
implicit def view2typed(v: View): TypedViewHolder = new TypedViewHolder { def view = v }
}
Now we can use the above code
val myView = inflater.inflate(R.layout.my_view, container, false)
val myWebView = myView.findView(TR.id.my_webview)
myWebView.loadUrl("http://www.google.com/")
val myTextView = myView.findView(TR.id.my_textview)
myTextView.setText("Google.com")
Putting both things together
class MyFragment extends Fragment {
private var myWebView: Option[WebView] = None
private var myTextView: Option[TextView] = None
override def onCreateView(
inflater: LayoutInflater,
container: ViewGroup,
savedInstanceState: Bundle): View = {
//imports the implicit conversion
import TypedResource._
val myView = inflater.inflate(R.layout.my_view, container, false)
myWebView = Some(myView.findView(TR.id.my_webview))
//now we're using options, so we must call methods on the inner value
//we can use Option.map(...) to do it [see http://www.scala-lang.org/api/current/index.html#scala.Option]
myWebView.map(_.loadUrl("http://www.google.com/"))
myTextView = Some(myView.findView(TR.id.my_textview))
//same as above
myTextView.map(_.setText("Google.com"))
myView
}
}
I hope this helps you out. I'm no expert with android so I can only get so far.
Why don't you simply write the fragment in Scala without bothering about "the best way to do this in Scala"? There might be none.
I'd start with removing public from the class definition and including the other goodies - TypedActivity from the article - in the activity. Then, set up the development environment - the IDE - and run the application. If it works, you're done (with the very first step in the migration). I don't think you need lazy val's from the very beginning.
Do small steps so the migration's easier.
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.