User Interface Development CSE 5236: Mobile Application Development Instructor: Adam C. Champion Course Coordinator: Dr. Rajiv Ramnath 1
Outline UI Support in Android Fragments 2
UI Support in the Android SDK Inverted paradigm Each subclass constrains rather than extends functionality Hundreds of methods are exposed Augh!! Base classes: ViewGroup base class for composite elements View base class for terminal UI components 3
View Hierarchy # = SDK Version number 4
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 if handle to the widget is needed Parameters: layout_width, layout_height layout_margintop,...right, layout_margin orientation Can be combined: layout_gravity= bottom right Constants: match_parent, wrap_content Width and height specifications: dp, in, mm, px, sp (scaled pixels based on font size) 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 Resources can also be added by rightclicking on res directory, selecting New Android Resource File 9
Other Layout Parameters and Techniques Inherit parameters from enclosing elements layout_span - to span multiple columns Empty views to add blank canvases to be filled later (see later) Shrink or stretch columns as needed: shrinkcolumns, stretchcolumns RelativeLayout allows window manager to manage size 10
Linking a UI to a Fragment: Java // LoginActivity.java public class LoginActivity extends SingleFragmentActivity { @Override protected Fragment createfragment() { return new LoginFragment(); // LoginFragment.java @Override public View oncreateview(layoutinflater inflater, ViewGroup container, Bundle savedinstancestate) { View v = inflater.inflate(r.layout.fragment_login, container, false); // Real code handles rotation musernameedittext = (EditText) v.findviewbyid(r.id.username_text); mpasswordedittext = (EditText) v.findviewbyid(r.id.password_text); Button loginbutton = (Button) v.findviewbyid(r.id.login_button); loginbutton.setonclicklistener(this); Button cancelbutton = (Button) v.findviewbyid(r.id.cancel_button); cancelbutton.setonclicklistener(this); Button newuserbutton = (Button) v.findviewbyid(r.id.new_user_button); newuserbutton.setonclicklistener(this); return v; 11 You can also set up listener objects in Activities using 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 rotation musernameedittext = v.findviewbyid<edittext>(r.id.username_text) mpasswordedittext = v.findviewbyid<edittext>(r.id.password_text) val loginbutton = v.findviewbyid<button>(r.id.login_button) loginbutton?.setonclicklistener(this) val cancelbutton = v.findviewbyid<button>(r.id.cancel_button) cancelbutton?.setonclicklistener(this) val newuserbutton = v.findviewbyid<button>(r.id.new_user_button) newuserbutton?.setonclicklistener(this) 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.board android:id="@+id/board" android:layout_width="match_parent" android:layout_height="280dip"/>... </LinearLayout> 13
Creating a Custom Widget: Java // Board.java public 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); @Override protected void ondraw(canvas canvas) { super.ondraw(canvas); //... 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); 14
Creating a Custom Widget: Kotlin // Board.kt class Board(context: Context, attributes: AttributeSet) : View(context, attributes) { init { isfocusable = true isfocusableintouchmode = 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) //... 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) 15
Creating a Layout via Code // DohActivity.java @Override public 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
Use: <EditText Definition: Styles and Themes (1) style= @style/darkbold android:text= Hello /> <?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> Inheritance: <style name= DarkPhone parent= @style/darkbold > <item name= android:phonenumber >true</item> </style> 17
Styles and Themes (2) Stored in res/values/ directory with.xml extension (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.html 18
Outline UI Support in Android Fragments 19
Fragments and Their Rationale A composite UI component that handles its own UI Multiple Fragments in an Activity A separate class hierarchy: Fragment, DialogFragment, ListFragment, PreferenceFragment, WebViewFragment Goals: Further separate UI from Activity. Separate UI design from Activity design UI should have its own lifecycle and flow Should be able to add or remove UI components while activity is running Primary driver: Tablets! 20
Example Login and Account
Portrait Layout: Login <?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"...> <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> 22
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 AccountFragment? 23
Landscape Layout: Login <?xml version="1.0" encoding="utf-8"?> <LinearLayout... android:orientation="horizontal...> NOTE HORIZONTAL LAYOUT <ScrollView... > <LinearLayout.. > <TextView... /> <TextView... /> <EditText.../> <TextView.../> <EditText.../> <Button.../> <Button.../> </LinearLayout> </ScrollView> <fragment class="com.wiley.fordummies.androidsdk.tictactoe.accountfragment" android:id="@+id/titles" android:layout_weight="1" android:layout_width="0px" android:layout_height="match_parent" android:background="#00550033"/> </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 @Override public 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); Button loginbutton = (Button) v.findviewbyid(r.id.login_button); if (loginbutton!= null) { loginbutton.setonclicklistener(this); Button cancelbutton = (Button) v.findviewbyid(r.id.cancel_button); if (cancelbutton!= null) { cancelbutton.setonclicklistener(this); Button newuserbutton = (Button) v.findviewbyid(r.id.new_user_button); if (newuserbutton!= null) { newuserbutton.setonclicklistener(this); return v; 27
LoginFragment: Kotlin override fun oncreateview(inflater: LayoutInflater, container: ViewGroup?, savedinstancestate: Bundle?): View? { val v: View val rotation = activity.windowmanager.defaultdisplay.rotation 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 = v.findviewbyid<edittext>(r.id.username_text) mpasswordedittext = v.findviewbyid<edittext>(r.id.password_text) val loginbutton = v.findviewbyid<button>(r.id.login_button) loginbutton?.setonclicklistener(this) val cancelbutton = v.findviewbyid<button>(r.id.cancel_button) cancelbutton?.setonclicklistener(this) val newuserbutton = v.findviewbyid<button>(r.id.new_user_button) newuserbutton?.setonclicklistener(this) return 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); etusername= (EditText)v.findViewById(R.id.username); etpassword= (EditText)v.findViewById(R.id.password); etconfirm = (EditText) v.findviewbyid(r.id.password_confirm); View btnadd= (Button)v.findViewById(R.id.done_button); btnadd.setonclicklistener(this); View btncancel= (Button)v.findViewById(R.id.cancel_button); btncancel.setonclicklistener(this); 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<edittext>(r.id.username) metpassword = v.findviewbyid<edittext>(r.id.password) metconfirm = v.findviewbyid<edittext>(r.id.password_confirm) val btnadd = v.findviewbyid<button>(r.id.done_button) btnadd.setonclicklistener(this) val btncancel = v.findviewbyid<button>(r.id.cancel_button) btncancel.setonclicklistener(this) return v 30
AccountFragment: onclick(): Java public void onclick(android.view.view v) { switch (v.getid()) { case R.id.login_button: checklogin(); break; case R.id.cancel_button: finish(); break; case R.id.new_user_button: startactivity(new Intent(this, Account.class)); break; 31
AccountFragment: onclick(): Kotlin override fun onclick(view: View) { when (view.id) { R.id.done_button -> createaccount() R.id.cancel_button -> { metusername.settext("") metpassword.settext("") metconfirm.settext("") 32
Thank You Questions and comments? 33