Tecnologías Móviles

clase 11

Ejercicio de la clase anterior

Menu Principal

Menú Contextual

Búsqueda

Ejercicio de la clase anterior

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="com.example.agustin.ej6.MainActivity">
    <item
        android:id="@+id/action_search"
        android:orderInCategory="1"
        android:title="@string/search"
        android:icon="@android:drawable/ic_menu_search"
        app:showAsAction="ifRoom"
        />
    <item
        android:id="@+id/action_settings"
        android:orderInCategory="100"
        android:title="@string/action_settings"
        app:showAsAction="never" />
</menu>

/res/menu/menu_main.xml

Ejercicio de la clase anterior

MainActivity

public class MainActivity extends AppCompatActivity {
     ...
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();

        if (id == R.id.action_settings) {
            ...
        } else if(id == R.id.action_search) {
            SearchFragment search = new SearchFragment();
            if(isPort) {
                FragmentTransaction transaction = manager.beginTransaction();
                transaction.replace(R.id.container, search);
                transaction.addToBackStack("search");
                transaction.commit();
            } else {
                FragmentTransaction transaction = manager.beginTransaction();
                transaction.replace(R.id.listContainer, search);
                transaction.replace(R.id.formContainer, null);
                transaction.addToBackStack("search");
                transaction.commit();
            }
        }

        return super.onOptionsItemSelected(item);
    }
}

Ejercicio de la clase anterior

SearchFragment

public class SearchFragment extends Fragment {
    ...

    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        final UserDBHelper udb = UserDBHelper.getInstance(getActivity());
        View root = inflater.inflate(R.layout.fragment_search, container, false);
        EditText search = (EditText) root.findViewById(R.id.search);
        ListView list = (ListView) root.findViewById(R.id.searchList);
        list.setAdapter(SearchAdapter.getInstance().setContext(getActivity()));
        search.addTextChangedListener(new TextWatcher() {
            ...
            public void afterTextChanged(Editable s) {
                if (s.length() > 0) {
                    JSONArray searchResult = udb.search(s.toString());
                    SearchAdapter.getInstance().setData(searchResult);
                } else {
                    SearchAdapter.getInstance().setData(new JSONArray());
                }
            }
        });
        return root;
    }
}

Ejercicio de la clase anterior

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="com.example.agustin.ej6.MainActivity">
    <item
        android:id="@+id/menu_delete"
        android:icon="@android:drawable/ic_menu_delete"
        android:title="@string/delete"
        app:showAsAction="always"/>
</menu>

/res/menu/context.xml

<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        ...
        <item name="windowActionModeOverlay">true</item>
    </style>
    ...
</resources>

/res/values/styles.xml

Ejercicio de la clase anterior

ListFragment

public class ListFragment extends Fragment {
    ...
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        ...
        list.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
        list.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {

            public void onItemCheckedStateChanged(ActionMode mode, int position,
                                                  long id, boolean checked) {
                int c = list.getCheckedItemCount();
                mode.setTitle(c > 1 ? c + " Seleccionados" : c + " Seleccionado");
            }

            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                switch (item.getItemId()) {
                    case R.id.menu_delete:
                        SparseBooleanArray checked = list.getCheckedItemPositions();
                        UserDBHelper.getInstance(getActivity()).deleteItems(checked);
                        mode.finish(); // Action picked, so close the CAB
                        return true;
                    default:
                        return false;
                }
            }
            ...
        });
        return root;
    }
}

Ejercicio de la clase anterior

ListFragment

public class ListFragment extends Fragment {
    ...
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        ...
        list.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
        list.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
            ...

            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                MenuInflater inflater = mode.getMenuInflater();
                inflater.inflate(R.menu.context, menu);
                return true;
            }

            public void onDestroyActionMode(ActionMode mode) {}

            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                return false;
            }
        });

        return root;
    }
}

Ejercicio de la clase anterior

UserDBHelper

public class UserDBHelper extends SQLiteOpenHelper {
    ...
    public void deleteItems(SparseBooleanArray checked) {
        ArrayList<JSONObject> items = new ArrayList<JSONObject>();
        for (int i = 0; i < checked.size(); i++) {
            int position = checked.keyAt(i);
            items.add(read(position));
        }
        for(JSONObject item:items) {
            doDelete(item);
        }
        UserAdapter.getInstance().notifyDataSetChanged();
    }
    private void doDelete(JSONObject user) {
        SQLiteDatabase db = this.getWritableDatabase();
        String selection = UserContract.UserTable._ID+" = ?";
        String[] args = {user.optString("_id")};
        db.delete(UserContract.UserTable.TABLE_NAME, selection, args);
        if(mService != null) {
            try {
                user.put("_id", user.getString(UserContract.UserTable.MONGO_ID));
                user.remove("mongo_id");
                mService.mBinder.rmUser(user);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }
    ...
}

ViewPager

Es un Layout que permite a los usuarios pasar páginas en dirección horizontal

 

Para generar las páginas, se debe proveer una clase que extienda a PagerAdapter

 

Generalmente se utiliza en conjunto con Fragments, por eso existen clases estanda de adapters:

  1. FragmentPagerAdapter: Generalmente utilizado para un número finito de Fragments.
  2. FragmentStatePagerAdapter: Generalmente utilizado para cuando la cantidad de fragments se determina en runtime.

Proveer Swipe Navigation

1. Agregar un ViewPager dentro del Layout

<android.support.v4.view.ViewPager
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

2. Incluir un TabLayout en el Layout

<android.support.design.widget.TabLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/tab"/>

Proveer Swipe Navigation

3. Crear una clase que extienda a PagerAdapter

public class DemoCollectionPagerAdapter extends FragmentStatePagerAdapter {
    public DemoCollectionPagerAdapter(FragmentManager fm) {
        super(fm);
    }
    @Override
    public Fragment getItem(int i) {
        ...
        return fragment;
    }
    @Override
    public int getCount() {
        return 100;
    }
    @Override
    public CharSequence getPageTitle(int position) {
        return "OBJECT " + (position + 1);
    }
}

Proveer Swipe Navigation

4. Asignar el Adapter al ViewPager y asociar el ViewPager con el TabLayout

    public void onCreate(Bundle savedInstanceState) {
        ...
        mDemoCollectionPagerAdapter =
                new DemoCollectionPagerAdapter(
                        getSupportFragmentManager());
        mViewPager = (ViewPager) findViewById(R.id.pager);
        mViewPager.setAdapter(mDemoCollectionPagerAdapter);
        TabLayout tab = (TabLayout) findViewById(R.id.tab);
        tab.setupWithViewPager(mViewPager);
    }

Ejercicio

Basado en el ejercicio de la clase anterior, realizar los siguientes puntos teniendo en cuenta la base a donde apunta la api (http://tm5-agmoyano.rhcloud.com/):

  • Agregar en las preferencias de la aplicación un mail con el cual se va a hacer login (POST /login con {"mail": "<mail usuario>"} como body)
     
  • Agregar un tab donde se van a mostrar los chats del usuario logueado (GET /chat)
     
  • Cuando se hace click en una persona, en vez de editar, se debería abrir un fragment con mensajes del chat (GET /chat/<id chat>/message).
     
  • Si no existe un chat con dicha persona, se debe crear uno (POST /chat con {"to": "<id de usuario a chatear>"} como body), para luego mostrar mensajes.

Ejercicio

  • El fragment del chat debería contener un EditText en donde agrego el mensaje, y un botón "enviar" para agregar mensajes al chat (POST /chat/<id de chat>/message con {"message": "<texto del mensaje>"} como body)

Tecnologías Móviles - Clase 11

By Agustin Moyano

Tecnologías Móviles - Clase 11

  • 681