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);
}
Related
I'm trying to establish data binding between two properties of an object in my view model and my UI.
The object has the two properties name and iconName which I can access through Getter-/Setter-Methods in the view model.
The name property is a simple String field that gets bound to an EditText component.
The iconName property, however, is a Resource Name of an XML file located in the drawable directory which is supposed to get bound as the source of an ImageView component.
Changing the source of the ImageView component manually was as easy as calling:
int resId = getResources().getIdentifier(iconName, "drawable", getPackageName());
selectedIconView.setImageResource(resId);
in the Activity class.
But now I'm not sure how to extract the resource ID inside the Binding Adapter to update the ImageView since I seem to have no Context inside my Binding Adapter.
public class SubjectAdapter {
#BindingAdapter("app:subjectName")
public static void setSubjectName(EditText view, SubjectEntity subject) {
view.setText(subject.getName());
}
#BindingAdapter("app:srcCompat")
public static void setSubjectIcon(ImageView view, SubjectEntity subject) {
String iconName = subject.getIconName();
// TODO: Set Image Resource of view
}
}
If you have any View, getting a Context is as easy as calling getContext() on the view.
#BindingAdapter("app:srcCompat")
public static void setSubjectIcon(ImageView view, SubjectEntity subject) {
String iconName = subject.getIconName();
Context context = view.getContext();
String packageName = context.getPackageName();
int resId = context.getResources().getIdentifier(iconName, "drawable", packageName);
view.setImageResource(resId);
}
Have your adapter function receiving the value (resource ID):
class MyViewModel : ViewModel() {
companion object {
#BindingAdapter("app:srcCompat")
#JvmStatic
fun setImageViewResource(imageView: ImageView, resource: Int) {
imageView.setImageResource(resource)
}
}
}
This code is called from the generated class so it doesn't have to be in your viewmodel necessarily.
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.
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)
Ok, first of all, I know you have seen this problem before, and I'll tell you why this is different. I have a class, DrawView (followed some Canvas tutorials) and it extends View. Ok, but I want a separate class to handle all the animations, so I can just call, for example, mainMenuAnimation() and it will draw it instead of coding it to the actual game loop. Well, if I create a class for holding the animations, Animations.java, and extend DrawView, I get an error from Eclipse:
Implicit super constructor DrawView() is undefined for default constructor. Must define an explicit constructor
The problem is, if I call the DrawView() constructor, it makes a new Animations.java, and so on. (Maybe I should define Animations a = new Animations()? Not sure if I would run into problems later on though). So, if I add an empty constructor in DrawView(), it gives me this error:
Implicit super constructor View() is undefined for default constructor. Must define an explicit constructor
I have no idea what to do, help?
Okay, the reason why I instanced Animations in the DrawView() constructor is because Animations' constructor has to be super(context) and the only way to access the context is through the DrawView() constructor.
DrawView constructor code:
Paint paint; //initialize EVERYTHING
Resources res;
Bitmap title;
Rect titleRect;
boolean inMainMenu, issetBackgroundDrawableSupported;
List<BitmapDrawable> mainMenuAnimation;
int mainMenuAnimationIndex = 0;
public DrawView(Context context) {
super(context);
res = getResources(); //required stuff
title = BitmapFactory.decodeResource(getResources(),R.drawable.title); //title stuff
titleRect = new Rect(res.getDisplayMetrics().widthPixels/2 - title.getWidth()*10 , 100, res.getDisplayMetrics().widthPixels/2 + title.getWidth()*10, 200); //left, top, right, bottom
inMainMenu = false; //main menu stuff
issetBackgroundDrawableSupported = true;
mainMenuAnimation = new ArrayList<BitmapDrawable>();
mainMenuAnimation.add(new BitmapDrawable(getResources(), BitmapFactory.decodeResource(res, R.drawable.mainmenu_background_1)));
mainMenuAnimation.add(new BitmapDrawable(getResources(), BitmapFactory.decodeResource(res, R.drawable.mainmenu_background_2)));
mainMenuAnimation.add(new BitmapDrawable(getResources(), BitmapFactory.decodeResource(res, R.drawable.mainmenu_background_3)));
Animations animations = new Animations(getApplication());
}
And the Animations.java code:
public class Animations extends DrawView {
//define animations
#SuppressLint("NewApi")
public void mainMenuScroll(Canvas canvas) {
inMainMenu = true;
//draw main menu here
if (inMainMenu = true) { //main menu loop
if (issetBackgroundDrawableSupported) { //check if background drawing is supported
try {
setBackgroundDrawable(mainMenuAnimation.get(mainMenuAnimationIndex));
} catch (Exception e){
issetBackgroundDrawableSupported = false; //say it is unsupported
setBackground(mainMenuAnimation.get(mainMenuAnimationIndex));
}
}
else {
setBackground(mainMenuAnimation.get(mainMenuAnimationIndex));
}
mainMenuAnimationIndex++;
if (mainMenuAnimationIndex == 3) { //restart main menu animation
mainMenuAnimationIndex = 0;
}
}
}
}
Ok, I realized another Eclipse notification, might be useful. It says:
Custom view com/spng453/agenericrpg/Animations is missing constructor used by tools: (Context) or (Context,AttributeSet) or (Context,AttributeSet,int)
Sounds relevant, but I'm not sure what to do about it.
All Views run within the context of a Context. (I guess that's why it's called that =P). This includes your custom View.
You're going to want to define an Animations constructor that takes a Context, so you can pass it through to the super constructors. This is the cleanest way to get rid of your errors, and will also fix the last problem you mentioned (namely, the Android system is trying to instantiate your class, but it doesn't know what to do with a View that doesn't take a Context in its constructor).
public Animations(Context context) {
super(context);
}
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.