I'm really confused about custom views.
I need to define a custom view, consists of an ImageView and a TextView. And then I want to change the contents of this views, according to a php json response, which I have accomplished.
First of all, which way should I go :
1) Define the custom view as an XML, then "inflate" , duplicate, whatever, and then change the newly instantiated text's and image sources etc ?
2) Define the custom view as a Java class, and instantiate it ?
In the end, I want to instantiate my custom views as children of a vertical layout.
What I'm currently trying is, path #2. I defined this class :
public class ArizaSatiri extends LinearLayout {
TextView arizaTitle;
//constructor :
public ArizaSatiri(Context context, AttributeSet attrs)
{
super(context, attrs);
// add title , description etc :
arizaTitle = new TextView(context);
arizaTitle.setText("abcef defefef");
this.addView(arizaTitle);
}
}
Then I tried this in my main activity :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_arizalarim);
ArizaSatiri as = new ArizaSatiri(getApplicationContext(), attrSet);
}
But I have no idea how to construct an AttributeSet.
So please tell me, which path should I choose, and how to accomplish to instantiate a custom view, as many times as I want, dynamically ?
Attribute set is constructed when you add your component via xml. You have to define custom component's attributes in attrs.xml. (http://www.vogella.com/articles/AndroidCustomViews/article.html#additional_attributes)
If you do not want to create your view from xml then just remove attribute set from the constructor as LinearLayout has a constructor without the attr set: http://developer.android.com/reference/android/widget/LinearLayout.html#LinearLayout(android.content.Context)
Related
One of my coworkers created an inherit from a RecyclerView and added the logic to create its adapter, defined custom list item attributes, and layout manager inside it.
This is an example of his idea:
class CustomRecyclerView #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : RecyclerView(context, attrs, defStyleAttr) {
init {
loadCustomAttributes()
setDefaultAdapter()
setDefaultLayoutManager()
}
private fun loadCustomAttributes() {
// Load custom attributes: item background, for example.
}
private fun setDefaultAdapter() {
// Define specific Custom Adapter.
}
private fun setDefaultLayoutManager() {
// Define specific Layout Manager.
}
fun setData(data: List) {
// Set data and notify dataset changed.
}
private class CustomAdapter : RecyclerView.Adapter<CustomAdapter.ViewHolder>() {
// Specific Adapter.
}
private class ViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
// Specific View Holder.
}
}
I understand his reasons, and it's very reasonable: he wants to plug a View on the XML and just reuse it, without defining the adapter or anything else on the Activity or Fragment; he will just set the data with the method "setData".
But on the other way, I feel that this class is inheriting a RecyclerView just to breaking the design of the RecyclerView and giving too many responsibilities to it. My pain points are:
A RecyclerView shouldn't decide an item attribute (item background colour, for example);
A RecyclerView shouldn’t know about his LayoutManager;
A RecyclerView shouldn't have methods to update the data on the adapter;
A RecyclerView shouldn’t create an instance of his Adapter.
This is an "inheritance over composition" approach.
I tried to find information about it on the internet, but I didn't find anything about good practices when inheriting from a ListView/RecyclerView.
Another idea would be to wrap it inside another layout (ViewGroup or FrameLayout) to encapsulate it. But this would create an unnecessary nested layout, and it will be complicated to test using Espresso (because now the list is private and shouldn't be exposed).
My question is: how to create a reusable RecyclerView using "Composition over Inheritance"?
I am attempting to build a dynamic control in Android that uses the builder pattern to construct a view that looks like:
[** Text][** Text]
Essentially, I am looking to create a view with an image and some text and group multiple such views together.
public static class DualBuilder {
private final Context mContext;
protected Drawable mFirstButtonIcon;
protected Drawable mSecondButtonIcon;
protected String mFirstButtonText;
protected String mSecondButtonText;
private DualBuilder(#NonNull final Context context) {
mContext = context;
}
public DualBuilder firstButtonIcon(#Nullable final Drawable firstButtonIcon) {
super.buttonIcon(firstButtonIcon);
return this;
}
public DualBuilder firstButtonText(#NonNull final String firstButtonText) {
super.buttonPrimaryText(firstButtonText);
return this;
}
public DualBuilder secondButtonIcon(#Nullable final Drawable secondButtonIcon) {
mSecondButtonIcon = secondButtonIcon;
return this;
}
public DualBuilder secondButtonText(#NonNull final String secondButtonText) {
mSecondButtonText = secondButtonText;
return this;
}
public MultiViewControl build() {
return new MultiViewControl(this);
}
}
The above would be a builder if i were to have two of these views. The constructor would take the builder variables and use them to construct a layout for each first/second view. The details of how that happens are not the issue, I just want to learn how to build something like this in a flexible way so it can accept 1...n number of views. What kind of a pattern can i use to achieve this? Any code sample would be very much appreciated.
Google has released a new library called Fluxbox layout which will serve your purpose.
check out this https://github.com/google/flexbox-layout
It will add your view dynamically side by side as you expect.
My two pence here...I just came up with this on the fly based on my experience.
So as I see it, your basic view content is an image and a string. If we consider this as an object structure called ViewContent then we can imagine that the Builder takes a composite of ViewContent objects and therefore we can pass 1..n such objects as a composite as input to the Builder object which will then proceed to act upon all these objects and create a layout for you...Hope this helps.
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 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.
I have a custom view:
public class Loading extends View {
private long movieStart;
private Movie movie;
public Loading(Context context, InputStream inputStream) {
super(context);
movie = Movie.decodeStream(inputStream);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE);
super.onDraw(canvas);
final long now = SystemClock.uptimeMillis();
if(movieStart == 0)
movieStart = now;
final int relTime = (int)((now - movieStart) % movie.duration());
movie.setTime(relTime);
movie.draw(canvas, 0, 0);
this.invalidate();
}
}
How can I use this view in XML layout? How can I pass the parameters (Context, InputStream) in XML layout?
How can I use this view in XML layout?
..
<pacakge_of_class.Loading
android:id="#+id/y_view1"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
http://developer.android.com/guide/topics/ui/custom-components.html
There is a form of the constructor that are called when the view is created from code and a form that is called when the view is inflated from a layout file. The second form should parse and apply any attributes defined in the layout file.
How can I pass the parameters
https://stackoverflow.com/a/4495745/804447
Error referencing an inner class View in layout/main.xml
<view class="Your_package.MainClass$Loading" />
The short answer is you can't directly do that.
The long answer is that you can indirectly do that.
Add the view to the XML by its fully qualified name (as others have mentioned), then:
What you need to do is implement the normal constructors from View. Define a custom attribute that declares the resource to use to create the InputStream in your constructor. The view system will give you the context automatically, you'd then need to open the InputStream based on the provided attribute value.
You can use a custom View in an XML-Layout like this:
<com.your.package.Loading
android:id="#+id/y_view1"
... />
But you cannot use your own constructor, you have to use the constructors as shown in this answer.
So you have to access your Loading View by code an set the InputStream manually:
Loading yourView = (Loading) findViewById(R.id.yourLoadingView);
yourView.setInputStream();
where you have this setter method in your Loading class:
public void setInputStream(InputStream inputStream){
movie = Movie.decodeStream(inputStream);
}