Linear Layout, Screen Support, and Events
Dec 30, 2015
Linear Layout, Screen Support, and Events
Linear Layout
• Supports 2 orientations:1. Horizontal2. Vertical
• I often get confused with how each orientation is laid out.
Horizontal Orientation
• One row, many columns
• Items placed beside each other
# # # # # # # # # #
Vertical Orientation
• One column, many rows
• Items stacked on top of each other
######
Gravity
• Two types of Gravity in Android1. gravity2. layout_gravity
• A lot of people get confused with the appropriate usage of each one.
android:gravity
• Positions the contents of the view (what is inside the view)
• Has several values:1. top2. bottom3. left4. right5. center (horizontal and vertical)6. center_vertical7. center_horizontal
android:gravity
• Use bit masking to combine multiple values
android:gravity="center_horizontal|bottom“
android:gravity=“top|bottom”
android:gravity=“top|right”
android:layout_gravity
• Positions the view with respect to its parent
• Supports same values as android:gravity
• Not all ViewGroups support this attribute.
gravity vs. layout_gravity
• I still don’t understand the difference…• Using gravity on a ViewGroup will position all
of its children a specific way.
Using a LinearLayout as the root view to encapsulate everything.
The LinearLayout has an attribute ofgravity with a value equal to center.(That is why all the button are centeredin the screen.)
gravity vs. layout_gravity
• Using layout_gravity, a child can override the gravity set on it by its parent.
Using a LinearLayout as the root view to encapsulate everything.
The blue button has an attribute oflayout_gravity with a value equal to right.(That is why all the button are centeredin the screen, except the blue button which is on the right.)
What does this render?<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width=“match_parent" android:layout_height="match_parent" >
<LinearLayout android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" >
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello" />
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/icon" /> </LinearLayout></LinearLayout>
What does this render?<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width=“match_parent" android:layout_height="match_parent" >
<LinearLayout android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" >
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello" />
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/icon" /> </LinearLayout></LinearLayout>
Why isn’t the content centered?<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width=“match_parent" android:layout_height="match_parent" >
<LinearLayout android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" >
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello" />
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/icon" /> </LinearLayout></LinearLayout>
What is the width of the LinearLayout?<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width=“match_parent" android:layout_height="match_parent" >
<LinearLayout android:orientation="horizontal"
android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" >
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello" />
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/icon" /> </LinearLayout></LinearLayout>
What is the width of the LinearLayout?<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width=“match_parent" android:layout_height="match_parent" >
<LinearLayout android:orientation="horizontal"
android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" >
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello" />
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/icon" /> </LinearLayout></LinearLayout>
Because the parent LinearLayout has width “wrap_content”, it’s essentially “hugging” the two views inside and so they have no space to center themselves in.
Adjust LinearLayout Width<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width=“match_parent" android:layout_height="match_parent" >
<LinearLayout android:orientation="horizontal"
android:layout_width=“match_parent" android:layout_height="wrap_content" android:gravity="center" >
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello" />
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/icon" /> </LinearLayout></LinearLayout>
A possible fix is to make the linearlayout as wide as its parent so use match_parent
Take 2: What does this render?<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width=“match_parent" android:layout_height=“match_parent" >
<LinearLayout android:orientation="horizontal"
android:layout_width=“match_parent" android:layout_height="wrap_content" >
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="left" android:text="@string/hello" />
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="right" android:src="@drawable/icon" /> </LinearLayout></LinearLayout>
Take 2: What does this render?<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width=“match_parent" android:layout_height=“match_parent" >
<LinearLayout android:orientation="horizontal"
android:layout_width=“match_parent" android:layout_height="wrap_content" >
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="left" android:text="@string/hello" />
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="right" android:src="@drawable/icon" /> </LinearLayout></LinearLayout>
Okay so my my parent fills the entire width and I tell the TextView to position itself with respect to its parent on the left, and I tell the ImageView to position itself with respect to its parent on the right, but how come it doesn’t work?
LinearLayouts and layout_gravity
• Depending on the orientation of the LinearLayout, the LinearLayout’s children can only use certain values for layout_gravity.
layout_gravity with horizontal orientation
• For a horizontal Linear Layout the following values make sense:– top– center– Bottom
• That is because the children of a horizontal Linear Layout are laid out horizontally one after the other.
• With horizontal orientation, the only thing can be controlled using the android:layout_gravity is how a child view is positioned vertically.
layout_gravity with vertical orientaiton
• For a vertical Linear Layout the following values make sense:– left– center– Right
• That is because the children of a vertical Linear Layout are laid out vertically one below the other.
• With vertical orientation, the only thing can be controlled using the android:layout_gravity is how a child view is positioned horizontally.
layout_gravity & gravity Example
layout_weight
• Attribute only works for LinearLayout
• Indicates how much of the extra space in the LinearLayout will be allocated to the view associated with this attribute.
layout_weight
• The size of the View will be determined based on the relation of all sibling’s weights to each other.
• If, for example, all views define the same weight, then the available space will be distributed equally among them.
layout_weight
• Which axis (width or height) should be affected can be controlled by setting the respective size to a value of 0dp.
• Weights don’t have to add up to 1, although it’s common to distribute layout weight over all children as fractions of 1 (percentage semantics).
• The relation between all weights is what matters.
Problem
• I have 4 Views whose width I want to be equally distributed inside their parent.
• I want this to work on any device, and I don’t want to calculate the width each time the app runs.
layout_weight to the rescue
• Using layout_weight we can assign an equal weight to each child view and at runtime the LinearLayout will equally distribute any remaining space to the children.
layout_weight to the rescue<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/pink" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" android:background="#00F" > <View android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight=".25" android:background="#0F0"/><View android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight=".25" android:background="#F00"/><View android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight=".25" android:background="#FC0"/><View android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight=".25" android:background="#FF0"/></LinearLayout>
Screen Support Key Terms
• Screen Density• Resolution• Orientation• Density-independent pixel
Screen Density
– The quantity of pixels within a physical area of the screen.
– Usually referred to as dpi– Android groups screen densities by: low, medium,
high, extra high– A low density screen has fewer pixels within a
given physical area than a “normal” or “high” density screen.
Orientation
– The orientation of the screen from the user’s point of view
– Either landscape or portrait
– You can create specific layouts catered to your devices orientation
Density Independent Pixel (dp)
– A virtual pixel unit
– Should be used for defining UI layout that can be reused for multiple screen sizes.
How big is a dp?
• Use the AndroidPixels App
• It shows for each device density type what 1dp is equivalent to in pixels.
Creating an emulator for a specific device
• Use this article
Handling UI Events
Topics1. Overview of UI Events2. Implementing UI Event Listeners
UI Events
• Key• Touch• Click• Long Click• Focus Change• Track ball• Drag and Drop
Key Events
Called when a hardware key is dispatched to focused a view.
Event Listener Interface: View.OnKeyListener
Callback Method: onKey()
Touch Events
This is called when the user performs an action qualified as a touch event:– Press– Release– Movement (within the bounds of a view).
Event Listener Interface: View.OnTouchListener
Callback Method: onTouch()
onTouch(View v, MotionEvent event)
• Takes 2 parameters
• Returns true if the listener consumed the event, false otherwise.
Name Description
v The view the touch event has been dispatched to
event The MotionEvent object containing full information about the event.
MotionEvent
• Used to report movement for– Mouse– Pen– Finger (Touch)– Trackball
MotionEvent for touch
• Contains data about “pointers” aka active touch points on the device’s screen.
• For each pointer you can receive– X/Y coordinates– Size– Pressure– Value describing what kind of motion occurred.
MotionEvent for single pointer
• getAction() – return the action being performed (action_down/move/up/cancel).
• getX() - returns the X coordinate of this event for a single pointer.
• getY() - returns the Y coordinate of this event for the given pointer
getAction()
• ACTION_DOWN - A touch gesture has started.• ACTION_MOVE - A change has occurred
between touch down and release.• ACTION_UP – A touch gesture has finished.• ACTION_CANCEL – A touch gesture has been
aborted.
@Overridepublic boolean onTouchEvent(MotionEvent ev) { final int action = ev.getAction(); switch (action) {
case MotionEvent.ACTION_DOWN: {final float x = ev.getX();final float y = ev.getY();
// Remember where we startedmLastTouchX = x;mLastTouchY = y;break;
}
case MotionEvent.ACTION_MOVE: {final float x = ev.getX();final float y = ev.getY();
// Calculate the distance movedfinal float dx = x - mLastTouchX;final float dy = y - mLastTouchY;
// Move the objectmPosX += dx;mPosY += dy;
// Remember this touch position for the next move eventmLastTouchX = x;mLastTouchY = y;
// Invalidate to request a redrawinvalidate();break;
} } return true;}
MotionEvent for multiple pointers
The MotionEvent class provides many methods to query the position and other properties of pointers:
getX(int) – returns X pos for given pointer index. getY(int) – returns Y pos for given pointer index. getPointerCount() – The number of pointers for
this event
MotionEvent for multiple pointers
getPointerId(int) – pointer id associated with the pointer index for this event
findPointerIndex(int) – given a pointer id, find the index of its data.
New getAction() values
• ACTION_POINTER_DOWN – fired when a secondary pointer goes down.
• ACTION_POINTER_UP – fired when a secondary pointer goes up.
Pointer index
• Each pointer index corresponds to a finger for the event.
• The pointer index ranges from 0 to one less than the returned value of getPointerCount().
• The order in which individual pointers appear is undefined; therefore you can’t rely on the pointer index to map to the same finger across touch events.
Multiple Pointers are hard!
• When working with multiple pointers, android provides many methods that use the pointer index.
• Pointer indices can change across events; therefore, we need another way to identify pointers.
Pointer ID
• The pointer id of a finger is guaranteed to remain constant as long as the pointer remains active (until it ACTION_UPs)
• When you want to keep track of a finger, save its ID on action down.
• Retrieve the saved finger’s pointer index for the current event with findPointerIndex(int).
Multiple Pointers Example
@Overridepublic boolean onTouchEvent(MotionEvent ev) { final int action = ev.getAction(); switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: { final float x = ev.getX(); final float y = ev.getY(); mLastTouchX = x; mLastTouchY = y;
// Save the ID of this pointer mActivePointerId = ev.getPointerId(0); break; } case MotionEvent.ACTION_MOVE: { // Find the index of the active pointer and fetch its position final int pointerIndex = ev.findPointerIndex(mActivePointerId); final float x = ev.getX(pointerIndex); final float y = ev.getY(pointerIndex); final float dx = x - mLastTouchX; final float dy = y - mLastTouchY; mPosX += dx; mPosY += dy; mLastTouchX = x; mLastTouchY = y; invalidate(); break; } case MotionEvent.ACTION_UP: { mActivePointerId = INVALID_POINTER_ID; break; } case MotionEvent.ACTION_CANCEL: { mActivePointerId = INVALID_POINTER_ID; break; } case MotionEvent.ACTION_POINTER_UP: { // Extract the index of the pointer that left the touch sensor final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; final int pointerId = ev.getPointerId(pointerIndex); if (pointerId == mActivePointerId) { // This was our active pointer going up. Choose a new // active pointer and adjust accordingly. final int newPointerIndex = pointerIndex == 0 ? 1 : 0; mLastTouchX = ev.getX(newPointerIndex); mLastTouchY = ev.getY(newPointerIndex); mActivePointerId = ev.getPointerId(newPointerIndex); } break; } } return true;}
Additional Info on Multitouch
• http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html
Click Events
• A Click is a pair of down/up events performed either with the keyboard, trackball, or touch screen.
• If the view receives a down event but does not receive a following up event, then the click event does not occur.
Click Events
Event Listener Interface: View.OnClickListener
Callback Method: onClick()
Parameters: v The view that has been clicked
Long Click Events
• Happens when a item is pressed and held (for one second).
• Applies to touch, trackball, hard keys.
Long Click Events
Event Listener Interface: View.OnLongClickListener
Callback Method: onLongClick()
Parameters: v The view that has been clicked and held
Trackball events
• To my knowledge, trackballs on devices have completely disappeared.
• So don’t worry about them.
Focus Change events
• Focus Change applies to physical keyboards.
• There are still a few devices that support a physical QWERTY keyboard.
• However, BlueTooth keyboards are becoming more popular and will work with any BlueTooth enabled device.
What is Focus?
• When using a physical keyboard, users can navigate through the UI using arrow keys.
• If a view has focus, a focus ring/rectangle will appear around the view.
• Focus also occurs with EditText views when they are selected.
Focus Example
Focus Tip
• Unless you’re using a physical hard keyboard or soft/virtual keyboard don’t worry about focus.
• Sometimes developers will try and use focus at inappropriate times so use the above tip.