I am making an android app, using Xamarin, that allows a user to add and then view various types of information. Currently I made an 'add item' screen for each type of information the user is able to store but that is currently 11 screens and when I add the display views then that is 11 more. Eventually that could grow to even more. Is there a better way to handle this sort of thing?
More Detail:
To do a very simple example using 2 items, lets say a user can store the following information about a Car:
Exterior Color
Interior Color
Amount of Doors.
Then lets say the user can store the following about a Building:
Amt of Floors
Address
Phone Number
For the car 'add item' screen I would want textviews and plainviews for each of that information and then lets say I would want a box at the bottom to allow a user to upload an image. Then for the building 'add item' screen i want to have, again, textviews and plainviews for each piece of information but this time I want there to be a box for notes at the bottom instead.
Again this is a simple example. In reality the screens look pretty different overall but serve the same functionality in the sense that they allow a user to add information, but that information is also much different then each other for the most part.
TLDR: What is the best way to handle screens that serve the same relative purpose but look differently? Do I make separate activities and layouts for each add screen or is there a way to use a master layout/activity that changes based on the type of item being added?
PS: I do not think using Xamarin plays any part in this but I wanted to mention it to be safe.
What is the best way to handle screens that serve the same relative purpose but look differently?
Abstract out the same part of the layouts. For example Both your "car" and "Building" layout should have "upload image" mode. when you design this kind of layout. You can use <include> tag as follows
<include
layout="#layout/upload_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
Dynamic layout is also suitable for you. For example both "car" and "Building" layout need some textView, your can write as following code:
public class DynamicLayout : LinearLayout
{
private List<LinearLayout> layouts;
public DynamicLayout(Context context, int textViewcount) : base(context)
{
this.Initialize(textViewcount);
}
protected override void OnSizeChanged(int w, int h, int oldw, int oldh)
{
base.OnSizeChanged(w, h, oldw, oldh);
foreach (var l in layouts)
{
l.LayoutParameters = new LinearLayout.LayoutParams(w / layouts.Count, ViewGroup.LayoutParams.WrapContent);
}
}
private void Initialize(int textViewcount)
{
this.Orientation = Orientation.Horizontal;
layouts = new List<LinearLayout>();
for (int n = 0; n < textViewcount; n++)
{
var layout = new LinearLayout(this.Context)
{
Orientation = Orientation.Vertical,
LayoutParameters =
new LinearLayout.LayoutParams(this.Width / textViewcount, ViewGroup.LayoutParams.WrapContent)
};
layouts.Add(layout);
if ("car")
{
var textView = new TextView(this.Context)
{
Text = "Car"
};
layout.AddView(textView);
}
else if ("building")
{
var textView = new TextView(this.Context)
{
Text = "building"
};
layout.AddView(textView);
}
this.AddView(layout);
}
}
}
But Dynamic layout is hard to control the display of the views. If the two methods are used in combination that could be better.
Related
I have a TextCell which adds a disclosure indicator in the iOS side of my Xamarin Forms app:
<TextCell Text="Japanese" StyleId="disclosure"/>
I would also like to do the same thing for the android side but I can't seem to find any custom renderers to do such thing.
Has anyone been able to do this? I would really appreciate if someone could point me to that direction.
According to design guidelines, it is recommended to not use right-pointing carets (disclosure indicators) on line items in Android.
Don't use right-pointing carets on line items
A common pattern on other platforms is the display of right-pointing carets on line items that allow the user to drill deeper into additional content.
Android does not use such indicators on drill-down line items. Avoid them to stay consistent with the platform and in order to not have the user guess as to what the meaning of those carets may be.
EDIT 1:
But if you still want to add the indicator, you can use an image for that.
Download the icon and add as drawable to android project. And register the renderer as following:
[assembly: ExportRenderer(typeof(TextCell), typeof(StandardTextCellRenderer))]
namespace AppNamespace.Droid
{
public class StandardTextCellRenderer : TextCellRenderer
{
protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView, Android.Views.ViewGroup parent, Android.Content.Context context)
{
var cell = base.GetCellCore(item, convertView, parent, context);
switch (item.StyleId)
{
case "disclosure":
var bmp = BitmapFactory.DecodeResource(cell.Resources, Resource.Drawable.ic_chevron_right);
var bitmapDrawable = new BitmapDrawable(cell.Resources, Bitmap.CreateScaledBitmap(bmp, 50, 50, true));
bitmapDrawable.Gravity = GravityFlags.Right | GravityFlags.CenterVertical;
cell.SetBackground(bitmapDrawable);
break;
}
return cell;
}
}
}
I have an activity A. I am creating a kind of tutorial for user for this activity, to teach him how he can use the app on that screen.
For that, my requirement is :
I want to blur all the views of the activity except one view. I want to prompt user to click on that view through a hand image pointing at that view.
Nothing should happen if the user clicks on the blurred/greyed out area, but if he taps on that particular active view, it should react to that touch.
I was thinking of using a full screen fragment for this. The Fragment will take the following input from the activity :
for what coordinates, is should not blur the screen and pass the touch event to the activity
the coordinates on which it should show that pointing hand image.
After from these coordinates, the fragment background would be blur.
I wanted to confirm if that's possible, to make the fragment partially active, i.e. delegate it's touch events to the activity for a particular view of the activity.
Also, please let me know if there is any other better approach of achieving the same thing.
Edit1 :
Thinking of using a fragment here, because I'd want this type of behaviour on different screen in future. In that case, I'd make that fragment generic which takes some inputs (as described above) and use it on different screens.
There's a very good library called SCV which does what you're trying to achieve, you're able to customize the styles for it too. I've used this for first time the app is opened to show the user a tutorial.
According to their Github
The ShowcaseView (SCV) library is designed to highlight and showcase specific parts of apps to the user with a distinctive and attractive overlay. This library is great for pointing out points of interest for users, gestures, or obscure but useful items.
Further Reading:
Android Arsenal - Showcase Views Tutorial
ShowCaseView on Android - Indipendev
I found it much easier to include an 'extra' layout around the UI of my activity, and then to add a highest-z grey mostly-transparent filter to it and put the instructions on that.
Each "step" of the instructions was a different layout that was dynamically loaded into that layout container as they clicked. (Just another approach)
The 'container' layout is a: FrameLayout
then in my Activity I have: (ignore bad naming)
private void addOverlayLayout() {
frameLayout = (FrameLayout) findViewById(R.id.framelayoutInner);
frameLayout3 = (FrameLayout) findViewById(R.id.framelayout3);
frameLayout3.setBackgroundColor(Color.DKGRAY);
frameLayout3.setAlpha(0.3f);
// Dynamically create a relativelayout which will be appended to framelayout
relativeLayout = new RelativeLayout(getApplicationContext());
relativeLayout.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams
.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
instructionOverlays.add(createSimpleClickInstruction(R.layout.instruction_reader_1));
instructionOverlays.add(createSimpleClickInstruction(R.layout.instruction_reader_2));
if (FullscreenReaderActivity.isFirstRun) {
displayNextGuide();
}
}
public void displayNextGuide() {
// clean relative layout if it has views
relativeLayout.removeAllViews();
// clean frame layout if it has child (safe if empty)
frameLayout.removeView(relativeLayout);
if (!isFirstRun) {
return;
}
if (instructionOverlays.size() > 0) {
runOnUiThread(instructionOverlays.get(0));
instructionOverlays.remove(0);
} else {
frameLayout3.setBackgroundColor(Color.TRANSPARENT);
frameLayout3.setAlpha(1.0f);
}
}
public Runnable createSimpleClickInstruction(final int resource) {
return new Runnable() {
#Override
public void run() {
getLayoutInflater().inflate(
resource,
relativeLayout,
true
);
relativeLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
displayNextGuide();
}
});
frameLayout.addView(relativeLayout);
}
};
}
We are developing an Android application where the requirement is to have single image having different navigation when the different parts of the image are clicked. E.g. A full image of a building and clicking on the different flats we want to show the amenities offered in the flat ( build up area, number of rooms, number of balconies, terrace (if pent house) etc).
Have thought the following two possibilities,
Slice the images and put together (such that it look as a single image) in layout xml, once user clicks it, with the image view id we will know which portion is clicked.
Slice the images and put together (such that it look as a single image) in html with onclick event, load the html in webview, create JavaScript class that handles clicks.
With above two it is difficult because the parts are not proper rectangle or square shaped.
ASP.Net has ImageMap control for this purpose, do we have anything of that sort in Android?
There is no such component to do this in android, but you can easily create one:
public class ImageMap extends ImageView implements onClickListener
{
List<Rect> hotspot;
public ImageMap(Context c)
{
super(c);
hotspot = new List<Rect>();
setOnClickListener(this);
}
public void addHotSpot(Rect r)
{
hotspots.add(r);
}
public void onClick(Event e)
{
int currentPosition = 0;
for(Rect spot : hotspot){
if(spot.contains(e.getX(), e.getY()))
triggerClickInHotSpotAtPos(currentPosition);
currentPosition ++;
}
}
public void triggerHotSpot(int hotspotIndex)
{
//TODO do something useful like calling listener for this given hotspot etc ...
}
}
be aware that is not a finished work, but its more like pseudo code, the important is you got the idea.
You could do this with a single ImageView by tapping into its onTouch event handler.
See the MotionEvent documentation for specifics on how to get coordinates of the touch event.
Good day everyone.
I am creating a calendar component, and I'm working in the month view. I have created a view named MonthView, and I am adding a couple instances of this to a ViewFlipper:
viewFlipper = new ViewFlipper(getContext());
viewFlipper.addView(new MonthView(viewFlipper.getContext()));
viewFlipper.addView(new MonthView(viewFlipper.getContext()));
I have implemented the fling gesture so that I change views when sliding my finger left or right. This will cyclically update and display the months.
Now, I need to give the fling gesture a smoothly effect when touching and slowly sliding my finger. The same we get when we use a Slider instead a ViewFlipper.
The problem with Scroller is that the effect is not cyclic. Once I get to the last view, I have to slide in the other direction.
I need someone help me find how to give a scroll-like effect to the ViewFlipper, or how to make a Scroller cyclic.
Thanks in advance.
Extra comment:
I have already implemented a ViewFlipper with 2 views. I update the views by using the SimpleOnGestureListener.onFling(...) method, and the behavior I got is something like this:
Imagine I always slide from rigth to left, like flipping a book's page to read the next one, and also imagine there is a caption in the header of the view that is displayed after flipping.
View # 0 --> Caption: January 2011
View # 1 --> Caption: Febrary 2011
View # 0 --> Caption: March 2011
View # 1 --> Caption: April 2011
View # 0 --> Caption: May 2011
If at this point I slide from left to right, the result will be something like:
View # 1 --> Caption: April 2011
View # 0 --> Caption: March 2011
The ability to cyclically move forward or backward, giving the user the idea of having infinite views, but using only a couple is characteristic of ViewFlipper, and that's what I can't loose.
That's why I need a way to add the cool scroll effect without loosing what I've got.
Thanks.
Then you can use ViewFlinger!
viewflinger this an android widget (extends ViewGroup) that allows to group a set of views that can be swiped horizontally. It offers a smoother transition that cannot be accomplished using ViewFlipper. This is based on the Workspace class on the iosched project, which is based in the class with the same name of the Launcher app.
Download: 1.0.2 | Sources | JavaDoc
If you use Maven, you can use it as an artifact from this repository: http://mvn.egoclean.com/. Also, you would want to look this video where I show how it looks like: http://www.youtube.com/watch?v=gqIXq5x7iLs (sorry for my accent which sucks)
I think what you wanted to do is create an apparently infinite list of layouts being flinged by either the ViewFlipper or Christian's ViewFlinger. And also you want to keep reusing views / layouts inside the Flinger / Flipper. Right ?
If yes, probably the following is what you wanted to do. I've done this based on Christian's ViewFlinger,
Here you go,
First add three layouts to the ViewFlinger:
<com.egoclean.android.widget.flinger.ViewFlinger
android:id="#+id/calendarViewFlipper"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<ScrollView
android:id="#+id/calendarViewLayout0"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
</ScrollView>
<ScrollView
android:id="#+id/calendarViewLayout1"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
</ScrollView>
<ScrollView
android:id="#+id/calendarViewLayout2"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
</ScrollView>
</com.egoclean.android.widget.flinger.ViewFlinger>
Then inside your activity, you take an array of three views so that you can access them directly through the array instead of searching every time inside the flinger,
private ViewFlinger viewFlinger;
private ViewGroup layouts[] = new ViewGroup[3];
private boolean userEvent = false;
#Override
public final void onCreateSub(Bundle savedInstanceState)
{
setContentView(R.layout.your_layout);
viewFlinger = (ViewFlinger) findViewById(R.id.calendarViewFlipper);
layouts[0] = (ViewGroup) findViewById(R.id.calendarViewLayout0);
layouts[1] = (ViewGroup) findViewById(R.id.calendarViewLayout1);
layouts[2] = (ViewGroup) findViewById(R.id.calendarViewLayout2);
viewFlinger.setOnScreenChangeListener(new ViewFlinger.OnScreenChangeListener()
{
#Override
public void onScreenChanging(View newScreen, int newScreenIndex)
{
}
#Override
public void onScreenChanged(View newScreen, int newScreenIndex)
{
if (userEvent)
{
ViewGroup tempLayout = null;
if (newScreenIndex != 1)
{
// We don't want our actions to raise events and create a cyclic event chain
userEvent = false;
if (newScreenIndex == 2) // Scrolling towards right
{
tempLayout = layouts[0];
viewFlinger.removeViewFromFront();
viewFlinger.addViewToBack(tempLayout);
layouts[0] = layouts[1];
layouts[1] = layouts[2];
layouts[2] = tempLayout;
// Any other logic comes here...
}
else if (newScreenIndex == 0) // Scrolling towards left
{
tempLayout = layouts[2];
viewFlinger.removeViewFromBack();
viewFlinger.addViewToFront(tempLayout);
layouts[2] = layouts[1];
layouts[1] = layouts[0];
layouts[0] = tempLayout;
// Any other logic comes here...
}
// We switch the screen index back to 1 since the current screen index would change back to 1
viewFlinger.setCurrentScreenNow(1, false);
userEvent = true;
// And any other logic that you'd like to put when the swapping is complete May be fill the swapped view with the correct values based on its new location etc...
View result = refreshView(tempLayout.getChildAt(0));
if (result.getParent() != tempLayout)
{
((ViewGroup) result.getParent()).removeView(result);
tempLayout.removeAllViews();
tempLayout.addView(result);
}
}
}
}
});
}
I hope this is clear to you and helps you with your problem. It is working very fine for me! Should work fine for you too.
P.S. Thanks # Christian for the ViewFlinger, it is awesome. However it lacks some good onConfigurationChanged logic, if you get time do put something in :). The rest is the best !
I am writing Instrumentation tests for my android app. One thing I want to do is to find whether all the UI components are on the screen or not. For that I have taken the screen shot of the complete screen and then I am looking for a particular widget in that image.
This code need to be running on the device only, not on desktop.
E.g. the full screen shot (image-1) have various android components like textview, button, listview and a image. Now I have a subset of this image (image-2), suppose the image of the button.
How can I find that whether image-2 is part of image-1?
Assuming that this code is happening from within the application, it doesn't seem like image comparison is the easiest way to determine whether a view is visible.
If you are writing an external instrumentation application of some sort, and this answer doesn't apply, please let me know.
Here's what I would do to test for the presence of UI elements from within the app:
From the Android API docs on the View object: you can find a view by its ID that was set up in the XML file:
<Button
android:id="#+id/my_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/my_button_text"/>
In the App:
Button myButton = (Button) findViewById(R.id.my_button);
Then, check the getVisibility() and getGlobalVisibleRect (Rect r, Point globalOffset), both documented on the View doc page.
Pseudocode:
int[] viewIds = {<known ids from xml>};
foreach(int viewId in viewIds) {
View v = findViewById(viewId);
if (v!=null) {
bool isVisible = (v.getVisibility()==VISIBLE) && getGlobalVisibleRect(new Rect(), new Point());
// do something with the visible/invisible info
}
}