user interface developmentweb.cse.ohio-state.edu/~champion.17/5236/07_uiandroid.pdf · ui support...
Post on 16-Apr-2020
4 Views
Preview:
TRANSCRIPT
User Interface Development
CSE 5236: Mobile Application DevelopmentInstructor: Adam C. Champion, Ph.D.Course Coordinator: Dr. Rajiv RamnathReading: Big Nerd Ranch Guide, Chapter 7 (Fragments)
1
Outline
• UI Support in Android• Fragments
2
UI Support in the Android SDK
• “Inverted” paradigm– Each subclass constrains functionality (rather
than extend it)–Hundreds of methods are exposed: L
• Base classes:– ViewGroup base class for composite UI
elements– View base class for terminal UI components
3
View Hierarchy
4
# = SDK Version number
ViewGroup Hierarchy• Direct Subclasses:
– AbsoluteLayout– AdapterView<T extends Adapter>– FragmentBreadCrumbs– FrameLayout– GridLayout– LinearLayout– PagerTitleStrip– RelativeLayout– SlidingDrawer– ViewPager
• 19 indirect subclasses. See: – http://developer.android.com/reference/android/view/ViewGroup.html
5
Sample Layout, Login Activity<?xml version="1.0" encoding="utf-8"?><ScrollView xmlns:android="http://schemas.android.com/apk/res/android”
android:background="@color/background"android:orientation="horizontal"android:layout_width="fill_parent"android:layout_height="fill_parent"android:padding="20dip">
<LinearLayout android:orientation="vertical...<TextView android:text="@string/login_title” ... /><TextView ... /><EditText android:id="@+id/username_text” ... /><TextView ... /><EditText ... /><Button ... /><Button ... /><Button ... />
</LinearLayout></ScrollView>
Layout Configuration• ID: android:id="@+id/username_text” : used for widget handle• Parameters:
– layout_width, layout_height
– layout_marginTop / ...marginRight, layout_margin– orientation
• Can be combined: layout_gravity=“bottom|right”• Constants: match_parent, wrap_content• Width and height specs: dp, sp (display pixels, scaled pixels)• A wide range of LayoutParams• Resources (e.g. backgrounds):
android:background=“@drawable/backdrop”• Blank canvases using a (generic) View element in the layout 7
8
Adding Resources
9
Resources can also be added by right-clicking on resdirectory, selecting New→AndroidResource File
Other Layout Parameters, Techniques
• Inherit parameters from enclosing elements• layout_span spans multiple columns• Empty views to add blank canvases (filled later)• Shrink or stretch columns as needed
(shrinkColumns, stretchColumns)
• RelativeLayout lets window manager handle size• ConstraintLayout specifies widget constraints for
flexible widget rendering
10
Linking a UI to a Fragment: Java// LoginActivity.java
public class LoginActivity extends SingleFragmentActivity {@Overrideprotected Fragment createFragment() { return new LoginFragment(); }
}
// LoginFragment.java
@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {View v = inflater.inflate(R.layout.fragment_login, container, false);
// Real code handles view rotationmUsernameEditText = (EditText) v.findViewById(R.id.username_text);mPasswordEditText = (EditText) v.findViewById(R.id.password_text);// Setup listener objects for buttonsreturn v;
}
11Also can set up listener objects in Activities with onCreate()
Linking a UI to a Fragment: Kotlin// LoginActivity.kt
class LoginActivity : SingleFragmentActivity() {override fun createFragment(): Fragment { return LoginFragment() }
}// LoginFragment.kt
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {val v: View = inflater.inflate(R.layout.fragment_login, container, false)// Real code handles view rotationmUsernameEditText = v.findViewById<EditText>(R.id.username_text)mPasswordEditText = v.findViewById<EditText>(R.id.password_text)// Set up listener objects for Buttons
return v}
12
Creating a Custom Widget: Layout<?xml version="1.0" encoding="utf-8"?><LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:background="#676767"android:gravity="center_horizontal"android:padding="20dip">
<com.wiley.fordummies.androidsdk.tictactoe.Boardandroid:id="@+id/board"android:layout_width="match_parent"android:layout_height="280dip"/>
...</LinearLayout>
13
Creating a Custom Widget: Java// Board.javapublic class Board extends View {
public Board(Context context, AttributeSet attributes) {super(context, attributes); // . . .setFocusable(true); setFocusableInTouchMode(true); // . . .
}// . . .protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh); // . . .}@Overrideprotected void onDraw(Canvas canvas) {
super.onDraw(canvas); // . . .}
}
14
Instantiating the view:// LoginFragment.java: onCreateView() and setupBoard()// onCreateView()v = inflater.inflate(R.layout.fragment_game_session, . . .);// setupBoard()mBoard = (Board) v.findViewById(R.id.board);
Creating a Custom Widget: Kotlin// Board.kt
class Board(context: Context, attributes: AttributeSet) : View(context, attributes) {init {
isFocusable = trueisFocusableInTouchMode = true // . . .
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {super.onSizeChanged(w, h, oldw, oldh) // . . .
}
override fun onDraw(canvas: Canvas) {super.onDraw(canvas) // . . .
}
}
15
Instantiating the view:// LoginFragment.kt, onCreateView() and setupBoard()// onCreateView()v = inflater.inflate(R.layout.fragment_game_session, container, false) // setupBoard()mBoard = (Board) v.findViewById(R.id.board)
Creating a Layout via Code// DohActivity.java@Overridepublic void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);LinearLayout layout = new LinearLayout(this);Button dohButton = new Button(this);dohButton.setText(“In case of meltdown, Push Me!”);layout.addView(dohButton);setContentView(layout);
}
16
Styles and Themes (1)Use:
<EditTextstyle=”@style/DarkBold”android:text=”Hello” />
Definition:<?xml version=”1.0” encoding=”utf-8”?><resources>
<style name=“DarkBold”><item name=”android:layout_width”>
match_parent</item><item name=“android:layout_height”>
wrap_content</item>
... more parameters ...</style>
</resources> 17
Inheritance:<style name=“DarkPhone” parent=”@style/DarkBold”>
<item name=”android:phoneNumber”>true</item></style>
Styles and Themes (2)
• Stored in res/values/ directory with .xmlextension (name not relevant)
• Can set application-wide and activity-specific styles (aka themes):– Set themes in AndroidManifest.xml on <application>
tag or <Activity> tag<application android:theme="@style/CustomTheme"><activity android:theme="@android:style/Theme.Translucent">
• Can even create version-specific layout files• Ref: http://developer.android.com/guide/topics/
ui/themes.html18
Outline
• UI Support in Android• Fragments
19
Fragments and Their Rationale
• Fragment: Composite UI component that handles its own UI
• One, multiple Fragments in an Activity• Separate class hierarchy: Fragment, DialogFragment, ListFragment, PreferenceFragment, etc.
• Goals: – Separate UI design from Activity design– UI should have its own lifecycle and flow– Dynamically add/remove UI components in running activity
• Drivers: heterogeneous devices, user experience20
Example – Login and Account
Portrait Layout: Login
22
<?xml version="1.0" encoding="utf-8"?><ScrollView ...>
<LinearLayout...><TextView android:text="@string/login_title".../><TextView android:text="@string/enter_username”.../><EditText android:id="@+id/username_text”.../><TextView android:text="Enter Password".../><EditText android:id="@+id/password_text".../><Button android:id="@+id/login_button".../><Button android:id="@+id/cancel_button”.../><Button android:id="@+id/new_user_button”.../>
</LinearLayout></ScrollView>
Portrait Layout: Account
<?xml version="1.0" encoding="utf-8"?><LinearLayout ...>
<FrameLayout .../> ⟸⟸ Placeholder for fragment<Button android:id="@+id/exit_button”.../>
</LinearLayout>
What happened to landscape layout of the Fragment?
23
Landscape Layout: Login<?xml version="1.0" encoding="utf-8"?>
<LinearLayout...
android:orientation="horizontal” ...> ⟸⟸ Note: horizontal layout<ScrollView ... >
<LinearLayout .. >
<TextView ... />
<TextView ... />
<!-- . . . -->
</LinearLayout>
</ScrollView><fragment class="com.wiley.fordummies.androidsdk.tictactoe.AccountFragment"
android:id="@+id/titles"
android:layout_height="match_parent” . . ./>
</LinearLayout>24
AccountFragment: Portrait Layout<?xml version="1.0" encoding="utf-8"?><LinearLayout ...>
<LinearLayout...><TextView android:text="New Account" .../><TextView android:text="Username”.../><EditText android:id="@+id/username”.../><TextView android:text="Password”.../><EditText android:id="@+id/password”.../><TextView android:text="Confirm Password" .../><EditText android:id="@+id/password_confirm”.../><Button android:id="@+id/cancel_button” "/><Button android:id="@+id/done_button”.../></LinearLayout>
</LinearLayout>25
AccountFragment: Landscape Layout<?xml version="1.0" encoding="utf-8"?><LinearLayout...>
<LinearLayout...><TextView android:text="New Account”.../><TextView android:text="Username”.../><EditText android:id="@+id/username”...”/><TextView android:text="Password" .../><EditText android:id="@+id/password”.../><TextView android:text="Confirm Password" .../><EditText android:id="@+id/password_confirm”.../><LinearLayout ... >
<Button android:id="@+id/cancel_button”.../><Button android:id="@+id/done_button”.../>
</LinearLayout></LinearLayout>
</LinearLayout>
26
LoginFragment: Java@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {View v;int rotation = getActivity().getWindowManager().getDefaultDisplay()
.getRotation();if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
v = inflater.inflate(R.layout.fragment_login_land, container, false);} else {
v = inflater.inflate(R.layout.fragment_login, container, false);}mUsernameEditText = (EditText) v.findViewById(R.id.username_text);mPasswordEditText = (EditText) v.findViewById(R.id.password_text);// Set up OnClickListeners for buttonsreturn v;
}
27
LoginFragment: Kotlinoverride fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {val v: Viewval rotation = activity.windowManager.defaultDisplay.rotationif (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
v = inflater.inflate(R.layout.fragment_login_land, container, false)} else {
v = inflater.inflate(R.layout.fragment_login, container, false)}mUsernameEditText = v.findViewById<EditText>(R.id.username_text)mPasswordEditText = v.findViewById<EditText>(R.id.password_text)// Set up onClickListeners for buttonsreturn v
}
28
AccountFragment: onCreateView(): Java
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.accountfragment, container, false);
Activity activity = getActivity();if (activity != null) {int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();mEtUsername = v.findViewById(R.id.username);mEtPassword = v.findViewById(R.id.password);mEtConfirm = v.findViewById(R.id.password_confirm);// OnClickListeners for Add, Cancel buttons
Button btnExit = v.findViewById(R.id.exit_button);if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
btnExit.setOnClickListener(this);} else { btnExit.setVisibility(View.GONE); }
return v;
}29
AccountFragment: onCreateView(): Kotlin
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {val v = inflater.inflate(R.layout.fragment_account, container, false)
mEtUsername = v.findViewById(R.id.username)mEtPassword = v.findViewById(R.id.password)mEtConfirm = v.findViewById(R.id.password_confirm)// Set up OnClickListeners for Add, Cancel buttons
val btnExit = v.findViewById(R.id.exit_button)
if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {btnExit.setOnClickListener(this)
} else {
btnExit.setVisibility(View.GONE)
}return v
}
30
AccountFragment: onClick(): Java
@Overridepublic void onClick(View view) {
switch (view.getId()) {case R.id.done_button:
createAccount();break;
case R.id.cancel_button:mEtUsername.setText(""); mEtPassword.setText(""); mEtConfirm.setText("");break;
case R.id.exit_button:FragmentActivity activity = getActivity();if (activity != null) {
activity.getSupportFragmentManager().popBackStack();}
}} 31
AccountFragment: onClick(): Kotlinoverride fun onClick(view: View) {
when (view.id) {R.id.done_button -> createAccount()R.id.cancel_button -> {
mEtUsername.setText("")mEtPassword.setText("")mEtConfirm.setText("")
}
R.id.exit_button -> activity?.supportFragmentManager?.popBackStack()
}}
32
Thank You
Questions and comments?
33
top related