user interface developmentweb.cse.ohio-state.edu/~champion.17/5236/lecture7_uiandroid.pdf · user...

Post on 17-Apr-2018

235 Views

Category:

Documents

4 Downloads

Preview:

Click to see full reader

TRANSCRIPT

User Interface Development

CSE 5236: Mobile Application DevelopmentInstructor: 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

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 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

9

Resources can also be added by right-clicking on resdirectory, selecting New→AndroidResource File

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.javapublic 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 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;}

11You can also set up listener objects in Activities using onCreate()

Linking a UI to a Fragment: Kotlin// LoginActivity.ktclass LoginActivity : SingleFragmentActivity() {

override fun createFragment(): Fragment {return LoginFragment()

}}// LoginFragment.ktoverride fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,

savedInstanceState: Bundle?): View? {val v: View = inflater.inflate(R.layout.fragment_login, container, false)// Real code handles rotationmUsernameEditText = 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.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()// onCreateViewv = inflater.inflate(R.layout.fragment_game_session, . . .);// setupBoard()mBoard = (Board) v.findViewById(R.id.board);

Creating a Custom Widget: Kotlin// Board.ktclass 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 .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

22

<?xml version="1.0" encoding="utf-8"?><ScrollView

xmlns:android="http://schemas.android.com/apk/res/android"...><LinearLayout...>

<TextViewandroid:text="@string/login_title".../>

<TextViewandroid:text="@string/enter_username”.../>

<EditTextandroid:id="@+id/username_text”.../>

<TextViewandroid:text="Enter Password".../>

<EditTextandroid:id="@+id/password_text".../>

<Buttonandroid:id="@+id/login_button".../>

<Buttonandroid:id="@+id/cancel_button”.../>

<Buttonandroid: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 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@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);

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: 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)

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 fragmentView 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

top related