Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
11/13/2015 Fragment Lifecycle Exam 2 Review Akhilesh Tyagi Design Flexibility public class MySkeletonFragment extends Fragment { // Called when the Fragment is attached to its parent Activity. @Override public void onAttach(Activity activity) { super.onAttach(activity); // Get a reference to the parent Activity. } An example of how two UI modules defined by fragments can be combined into one activity for a tablet design, but separated for a handset design ● Using fragments in activity XML Activity layout XML can include fragments. <!‐‐ activity_name.xml ‐‐> <LinearLayout ...> <fragment ... android:id="@+id/id1" android:name="ClassName1" /> <fragment ... android:id="@+id/id2" android:name="ClassName2" /> </LinearLayout> // Called to do the initial creation of the Fragment. @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Initialize the Fragment. } • onCreate(): The system calls this when creating the fragment. Within your implementation, you should initialize essential components of the fragment that you want to retain when the fragment is paused or stopped, then resumed. • onCreateView(): The system calls this when it's time for the fragment to draw its user interface for the first time. To draw a UI for your fragment, you must return a View from this method that is the root of your fragment's layout. You can return null if the fragment does not provide a UI. Marty Stepps, Stanford CS193A 1 11/13/2015 // Called once the Fragment has been created in order for it to // create its user interface. @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Create, or inflate the Fragment's UI, and return it. // If this Fragment has no UI then return null. return inflater.inflate(R.layout.my_fragment, container, false); } Resource file my_fragment.xml Should the inflated layout be attached To the container? Fragment template public class Name extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup vg, Bundle bundle) { // load the GUI layout from the XML return inflater.inflate(R.layout.id, vg, false); } public void onActivityCreated(Bundle savedState) { super.onActivityCreated(savedState); // ... any other GUI initialization needed } // Called at the end of the active lifetime. @Override public void onPause(){ // Suspend UI updates, threads, or CPU intensive processes that don't //need to be updated when the Activity isn't the active foreground // activity. Persist all edits or state changes as after this call the process is likely to be killed. super.onPause(); } // Called to save UI state changes at the // end of the active lifecycle. @Override public void onSaveInstanceState(Bundle savedInstanceState) { // Save UI state changes to the savedInstanceState. // This bundle will be passed to onCreate, onCreateView, and // onCreateView if the parent Activity is killed and restarted. super.onSaveInstanceState(savedInstanceState); } XML Fragment layouts • Fragment element well suited for static layouts • If the layout is to be dynamically modified in the program, use containers to pass to fragment managers. // any other code (e.g. event‐handling) } Marty Stepps, Stanford CS193A // Called at the start of the visible lifetime. @Override public void onStart(){ super.onStart(); // Apply any required UI change now that the Fragment is visible. } // Called at the start of the active lifetime. @Override public void onResume(){ super.onResume(); // Resume any paused UI updates, threads, or processes required // by the Fragment but suspended when it became inactive. } <?xml version="1.0" encoding="UTF‐8"?> <LinearLayout android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="horizontal" xmlns:android="http://schemas.android.com/apk/res/android "> <FrameLayout android:layout_height="match_parent" android:layout_width="match_parent" android:layout_weight="1" android:id="@+id/ui_container"/> <FrameLayout android:layout_height="match_parent" android:layout_width="match_parent" android:layout_weight="3" android:id="@+id/details_container"/> </LinearLayout> 2 11/13/2015 Fragment Transactions • To add, remove, and replace fragments within an activity. Fragment vs. activity ● Fragment code is similar to activity code, with a few changes: – FragmentManager fragManager = getFragmentManager(); FragmentTransaction transaction = fragManager.beginTransaction(); transaction.add(R.id.container, firstFragment); transaction.commit(); • • Many activity methods aren't present in the fragment, but you can call • getActivity to access the activity the fragment is inside of. – • Button b = (Button) findViewById(R.id.but); • Button b = (Button) getActivity().findViewById(R.id.but); Sometimes also use getView to refer to the activity's layout – Event handlers cannot be attached in the XML any more. :‐( – Passing information to a fragment (via Intents/Bundles) is trickier. – Fragment initialization code must be mindful of order of execution. ● ● ● ● Must be attached in Java code instead. The fragment must ask its enclosing activity for the information. Does it depend on the surrounding activity being loaded? Etc. Typically move onCreate code to onActivityCreated. Marty Stepps, Stanford CS193A public class MyFragmentActivity extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Inflate the layout containing the Fragment containers setContentView(R.layout.fragment_container_layout); FragmentManager fm = getFragmentManager(); // Check to see if the Fragment back stack has been populated // If not, create and populate the layout. DetailsFragment detailsFragment = (DetailsFragment) fm.findFragmentById(R.id.details_container); Fragment onClick listener ● Activity: <Button android:id="@+id/b1" android:onClick="onClickB1" ... /> ● Fragment: <Button android:id="@+id/b1" ... /> // in fragment's Java file Button b = (Button) getActivity().findViewById(r.id.b1); b.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // whatever code would have been in onClickB1 } }); Marty Stepps, Stanford CS193A Communicating with the Activity if (detailsFragment == null) { FragmentTransaction ft = fm.beginTransaction(); ft.add(R.id.details_container, new DetailsFragment()); ft.add(R.id.ui_container, new MyListFragment()); ft.commit(); } } } • a fragment can access the Activity instance with getActivity() and easily perform tasks such as find a view in the activity layout: View listView = getActivity().findViewById(R.id.list); • activity can call methods in the fragment by acquiring a reference to the Fragment from FragmentManager, using findFragmentById() ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.exampl e_fragment); 3 11/13/2015 Fragment that accepts parameters public class Name extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.name, container, false); } Intents ● intent: a bridge between activities; a way for one activity to invoke another – – – the activity can be in the same app or in a different app can store extra data to pass as "parameters" to that activity second activity can "return" information back to the caller if needed @Override public void onActivityCreated(Bundle savedState) { super.onActivityCreated(savedState); // extract parameters passed to activity from intent Intent intent = getActivity().getIntent(); int name1 = intent.getIntExtra("id1", default); String name2 = intent.getStringExtra("id2", "default"); // use parameters to set up the initial state ... } Marty Stepps, Stanford CS193A Communication between fragments ● ● One activity might contain multiple fragments. The fragments may want to talk to each other. – – Use activity's getFragmentManager method. its findFragmentById method can access any fragment that has an id. Activity act = getActivity(); if (act.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { // update other fragment within this same activity FragmentClass fragment = (FragmentClass) act.getFragmentManager().findFragmentById(R.id.id); fragment.methodName(parameters); } Marty Stepps, CS193A, Stanford Intent Object • The main information that can be contained in an intent object : • Component name: The name of the component that should handle the intent. • Action: An action to be preformed such as initiating a phone call in an activity or handling a low battery warning in a broadcast. • Data: Data to be acted on. • Category: Additional information about the component handling the intent such as a widget, home, preference. • Extras: Key value pairs containing additional information that should be passed to the component. • Flags: These are contained in the intent API and have many different functions for the system on how to handle the intent. Marty Stepps, Stanford CS193A Using Intents to Launch Activities Intents • To open a different application screen (Activity) in your application, call startActivity, passing in an Intent startActivity(myIntent); • The Intent can either explicitly specify the class to open, or include an action that the target should perform. Run time will choose the implicit Activity to open with “Intent resolution.” 4 11/13/2015 Intent Resolution • Intents can be divided into two groups: • Explicit intents: These designate a component by name and are generally used locally for internal messages. • Implicit intents: Do not name a target and are generally used by outside applications. • For implicit intents the Android system will search components using intent filters to find the best match. Implicit Intents and Late Runtime Binding • Implicit Intents are a mechanism that lets anonymous application components service action requests. • implicit Intent to use with startActivity, specifies an action to perform and, optionally, supplies the data on which to perform that action. if (foundPhoneNumer) { Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(“tel:555-2368”)); startActivity(intent); } Extracting extra data ● In the second activity that was invoked, you can grab any extra data that was passed to it by the calling activity. – You can access the Intent that spawned you by calling getIntent. – The Intent has methods like getExtra, getIntExtra, getStringExtra, etc. to extract any data that was stored inside the intent. public class SecondActivity extends Activity { ... public void onCreate(Bundle savedState) { super.onCreate(savedState); setContentView(R.layout.activity_second); Intent intent = getIntent(); String extra = intent.getExtra("name"); ... } } Explicit Intent with Data/Arguments Intent i = new Intent(this, ActivityB.class); i.putExtra("myString1", "This is a message for ActivityB"); i.putExtra("myInt1", 100); startActivity(i); • The data is received at the target activity as part of a Bundle object which can be obtained via a call to getIntent().getExtras(). • ActivityB retrieves the data: Bundle extras = getIntent().getExtras(); if (extras != null) { String myString = extras.getString("myString1"); int myInt = extras.getInt("myInt1"); } Intents and Manifests <?xml version="1.0" encoding="utf‐8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.ebookfrenzy.Intent1" android:versionCode="1" android:versionName="1.0" > <uses‐sdk android:minSdkVersion="10" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:label="@string/app_name" android:name="ActivityA" > <intent‐filter > <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent‐filter> </activity> <activity android:name="ActivityB" android:label="ActivityB" > </activity> </application> </manifest> • Subactivity ActivityB returns the result with: public void finish() { Intent data = new Intent(); data.putExtra("returnString1", "Message to parent activity"); setResult(RESULT_OK, data); super.finish(); } Marty Stepps, CS193A, Stanford 5 11/13/2015 ActivityA receiving results protected void onActivityResult(int requestCode, int resultCode, Intent data) { String returnString; if (resultCode == RESULT_OK && requestCode == REQUEST_CODE) { if (data.hasExtra("returnString1")) { returnString = data.getExtras().getString("returnString1"); } } } Intent Filters • Because intent filters must be seen by the Android system before launching a component; they are often placed in the Android Manifest file instead of Java Code. • Implicit intents are tested against three filters of a component to see if they are correct. Category • category Use the android:category attribute to specify under which circumstances the action should be serviced ALTERNATIVE: The alternative category specifies that this action should be available as an alternative to the default action. BROWSABLE: Specifies an action available from within the browser DEFAULT: Set this to make a component the default action. This is also necessary for Activities that are launched using an explicit Intent GADGET: By setting the gadget category, you specify that this Activity can run embedded inside another Activity. HOME: The home Activity is the first Activity displayed when the device starts LAUNCHER: Using this category makes an Activity appear in the application launcher. • Action • Category • Data Actions ACTION_CALL activity Initiate a phone call. ACTION_EDIT activity Display data for the user to edit. ACTION_MAIN activity Start up as the initial activity of a task, with no data input and no returned output. ACTION_SYNC activity Synchronize data on a server with data on the mobile device. ACTION_BATTERY_LOW broadcast receiver A warning that the battery is low. ACTION_HEADSET_PLUG broadcast receiver A headset has been plugged into the device, or unplugged from it. ACTION_SCREEN_ON broadcast receiver The screen has been turned on. ACTION_TIMEZONE_CHANGED broadcast receiver The setting for the time zone has changed. Data • Data tag specifies the data types handled by this component. • android:host – specifies a valid host name (iastate.edu) • android:mimetype • android:path – valid path values for URIs • android:port – valid ports for a host 6 11/13/2015 Intent Filter Example <activity android:name=”.EarthquakeDamageViewer” android:label=”View Damage”> <intent‐filter> <action android:name=”com.paad.earthquake.intent.action.SHOW_DAM AGE”> </action> <category android:name=”android.intent.category.DEFAULT”/> <category android:name=”android.intent.category.ALTERNATIVE_SELECTED” /> <data android:mimeType=”vnd.earthquake.cursor.item/*”/> </intent‐filter> </activity> Listening for Broadcasts with Broadcast Receivers public class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { //TODO: React to the Intent received. }} • To enable a Broadcast Receiver, it needs to be registered, either in code or within the application manifest. When registering a Broadcast Receiver, you must use an Intent Filter to specify which Intents it is listening for. public class LifeformDetectedBroadcastReceiver extends BroadcastReceiver { public static final String BURN = “com.paad.alien.action.BURN_IT_WITH_FIRE”; @Override public void onReceive(Context context, Intent intent) { // Get the lifeform details from the intent. Uri data = intent.getData(); String type = intent.getStringExtra(“type”); double lat = intent.getDoubleExtra(“latitude”, 0); double lng = intent.getDoubleExtra(“longitude”, 0); Location loc = new Location(“gps”); loc.setLatitude(lat); loc.setLongitude(lng); if (type.equals(“alien”)) { Intent startIntent = new Intent(BURN, data); startIntent.putExtra(“latitude”, lat); StartIntent.putExtra(“longitude”, lng); context.startActivity(startIntent); }}} Registering Broadcast Receivers in Application Manifest <receiver android:name=”.LifeformDetectedBroadcastRec eiver”> <intent‐filter> <action android:name=”com.paad.action.NEW_LIF EFORM”/> </intent‐filter> </receiver> Registering Broadcast Receivers in Code // Create and register the broadcast receiver. IntentFilter filter = new IntentFilter(NEW_LIFEFORM_DETECTED); LifeformDetectedBroadcastReceiver r = new LifeformDetectedBroadcastReceiver(); registerReceiver(r, filter); Preferences storage • A SharedPreferences object points to a file containing key‐value pairs and provides simple methods to read and write them. • getSharedPreferences() — Use this if you need multiple shared preference files identified by name, which you specify with the first parameter. You can call this from any Context in your app. • getPreferences() — Use this from an Activity if you need to use only one shared preference file for the activity. Because this retrieves a default shared preference file that belongs to the activity, you don't need to supply a name. 7 11/13/2015 SharedPreferences files • accesses the shared preferences file that's identified by the resource string R.string.preference_file_key and opens it using the private mode so the file is accessible by only your app. When naming your shared preference files, you should use a name that's uniquely identifiable to your app, such as "com.example.myapp.PREFERENCE_FILE_KEY" • Context context = getActivity(); SharedPreferences sharedPref = context.getSharedPreferences( getString(R.string.preference_file_key), Context.MODE_PRIVATE); Read from Shared Preferences • call methods such as getInt() and getString(), providing the key for the value you want, and optionally a default value to return if the key isn't present SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE); int defaultValue = getResources().getInteger(R.string.saved_high_score_ default); long highScore = sharedPref.getInt(getString(R.string.saved_high_score) , defaultValue); Nameless Activity SharedPrerences file boolean isTrue = mySharedPreferences.getBoolean(“isTrue”, false); float lastFloat = mySharedPreferences.getFloat(“lastFloat”, 0f); int wholeNumber = mySharedPreferences.getInt(“wholeNumber”, 1); long aNumber = mySharedPreferences.getLong(“aNumber”, 0); stringPreference = mySharedPreferences.getString(“textEntryValue”, “”); • SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_P RIVATE); • If you create a shared preferences file with MODE_WORLD_READABLE or MODE_W ORLD_WRITEABLE, then any other apps that know the file identifier can access your data. SharedPreferences sharedPref = getActivity().getPreferences (Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPref.edit(); editor.putInt(getString(R.string.saved_high_score) , newHighScore); editor.putBoolean(“isTrue”, true); editor.putFloat(“lastFloat”, 1f); editor.putInt(“wholeNumber”, 2); editor.putLong(“aNumber”, 31); editor.putString(“textEntryValue”, “Not Empty”); editor.commit(); Multiple preference files ● You can call getSharedPreferences and supply a file name if you want to have multiple pref. files for the same activity: SharedPreferences prefs = getSharedPreferences( "filename", MODE_PRIVATE); SharedPreferences.Editor prefsEditor = prefs.edit(); prefsEditor.putInt("name", value); prefsEditor.putString("name", value); ... prefsEditor.commit(); Marty Stepps, CS193A, Stanford 8 11/13/2015 Putting it all together – Accelerometer mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); if (mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY) != null){ List<Sensor> gravSensors = mSensorManager.getSensorList(Sensor.TYPE_GRAVITY); for(int i=0; i<gravSensors.size(); i++) { if ((gravSensors.get(i).getVendor().contains("Google Inc.")) && (gravSensors.get(i).getVersion() == 3)){ // Use the version 3 gravity sensor. mSensor = gravSensors.get(i); } } } public class sensorexample extends Activity implements SensorEventListener { private SensorManager senSensorManager; private Sensor senAccelerometer; Reference to system sensor service @Override protected void onCreate(Bundle savedInstanceState) { Reference to default accelerometer Need to check if it exists super.onCreate(savedInstanceState); setContentView(R.layout.activity_sensorexample); senSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); senAccelerometer = senSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); senSensorManager.registerListener(this, senAccelerometer, SensorManager.SENSOR_DELAY_NORMAL); Register the listener to call back when sensor event occurs } Called when accelerometer registers a new event such as motion along any axis else{ // Use the accelerometer. if (mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null){ mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); } else{ // Sorry, there are no accelerometers on your device. // You can't play this game. } } @Override public void onSensorChanged(SensorEvent sensorEvent) { Sensor mySensor = sensorEvent.sensor; if (mySensor.getType() == Sensor.TYPE_ACCELEROMETER) { Acceleration values along float x = sensorEvent.values[0]; the three axis float y = sensorEvent.values[1]; float z = sensorEvent.values[2]; } } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } Important – Sensors drain battery resource. Sensors keep acquiring data during onPause if not unregistered public class SensorActivity extends Activity implements SensorEventListener { private SensorManager mSensorManager; private Sensor mLight; @Override public final void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); mLight = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); protected void onPause() { super.onPause(); senSensorManager.unregisterListener(this); } Re-register Sensorevent listener to continue acquiring data during onResume protected void onResume() { super.onResume(); senSensorManager.registerListener(this, senAccelerometer, SensorManager.SENSOR_DELAY_NORMAL); } } 9