Im heutigen Beitrag wird die Smarthome App um die Abfrage von Ereignissen erweitert. Seit dem Tutorial "Ereignisse in Datenbank speichern & abfragen", kann der Smarthome-Server Ereignisse in der Datenbank speichern. Mit der Ap war es jedoch bis jetzt nicht möglich, diese Ereignisse abzufragen.
EventFragment implementieren
Als erstes musst du ein neues Fragment erstellen. Dazu klickst du mit rechts auf eine Java-Datei und wählst "New -> Fragment -> Fragment (Blank)". Gib dem Fragment den Namen "EventFragment", entferne die Haken bei "Include fragment factory methods?" und "Include interface callbacks?" und klickst auf "Finish". Wechsle zur Layout-Datei des EventFragments namens "fragment_event.xml" und füge dort dem FrameLayout die folgenden Attribute hinzu:android:background="@color/layoutBackground"
android:id="@+id/frame"
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/event_list"></android.support.v7.widget.RecyclerView>
<include
layout="@layout/loading_animation"
android:visibility="gone"
android:id="@+id/loading_animation"/>
<include
layout="@layout/empty_item"
android:visibility="gone"
android:id="@+id/empty_item"/>
private View eventView;
//RecyclerView
private RecyclerView.Adapter eventAdapter;
private GridLayoutManager glm;
private ArrayList<EventItem> eventItems;
private RecyclerView eventArray;
private View loadingAnimation;
private int visibleItemCount, totalItemCount, pastVisibleItems;
private boolean loading = false;
private int offset = 0;
private boolean endOfItems = false;
String type = "";
eventView = inflater.inflate(R.layout.fragment_event, container, false);
loadingAnimation = eventView.findViewById(R.id.loading_animation);
//Argumente abfragen
Bundle bundle = getArguments();
if(bundle != null && bundle.containsKey(MainActivity.EXTRA_TYPE)){
type = bundle.getString(MainActivity.EXTRA_TYPE);
}
eventArray = (RecyclerView) eventView.findViewById(R.id.event_list);
eventArray.setHasFixedSize(true);
glm = new GridLayoutManager(getContext(), getResources().getInteger(R.integer.event_column_count));
glm.setOrientation(GridLayoutManager.VERTICAL);
eventArray.setLayoutManager(glm);
eventArray.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if(dy > 0){
visibleItemCount = glm.getChildCount();
totalItemCount = glm.getItemCount();
pastVisibleItems = glm.findFirstVisibleItemPosition();
if(!loading){
if((visibleItemCount + pastVisibleItems) >= totalItemCount && !endOfItems){
loadEvents(offset, MainActivity.LOAD_ON_SCROLL, type);
}
}
}
}
});
eventItems = new ArrayList<>();
loadEvents(offset, MainActivity.LOAD_ON_START, type);
return eventView;
private class EventItem{
private String text, type;
private long timestamp;
public EventItem(String text, String type, long timestamp){
this.text = text;
this.type = type;
this.timestamp = timestamp;
}
/**
* Gibt den Text des Items zurück
* @return
*/
public String getText(){
return text;
}
/**
* Gibt den Typen des Items zurück
* @return
*/
public String getType(){
return type;
}
/**
* Gibt den Zeitstempel des Items zurück
* @return
*/
public long getTimestamp(){
return timestamp;
}
/**
* Gibt den Zeitstempel als formattierten String zurück
* @return
*/
public String getFormattedTime(){
return new SimpleDateFormat("dd.MM.yyyy HH:mm").format(new java.util.Date(timestamp*1000));
}
}
/**
* Fragt die Ereignisse vom Server ab
* @param os Verschiebung der Ergebnisse
* @param s Max-Anzahl der Ergebnisse
* @param t der Typ der abzufragenden Ereignisse
*/
public void loadEvents(int os, int s, String t){
loading = true;
if(os == 0){
loadingAnimation.setVisibility(View.VISIBLE);
}
else{
eventItems.add(null);
eventAdapter.notifyItemInserted(eventItems.size()-1);
}
final Map<String, String> requestData = new HashMap<>();
requestData.put("action", "getevents");
requestData.put("username", SaveData.getUsername(getContext()));
requestData.put("password", SaveData.getPassword(getContext()));
requestData.put("type", t);
requestData.put("limit", String.valueOf(s));
requestData.put("offset", String.valueOf(os));
HTTPRequest.sendRequest(getContext(), requestData, SaveData.getServerIp(getContext()), new HTTPRequest.HTTPRequestCallback() {
@Override
public void onRequestResult(String result) {
loadingAnimation.setVisibility(View.GONE);
loading = false;
boolean firstLoad = false;
if(offset == 0){
firstLoad = true;
}
else{
eventItems.remove(eventItems.size()-1);
eventAdapter.notifyItemRemoved(eventItems.size()-1);
}
if(result != null){
try{
JSONObject jsonObject = new JSONObject(result);
JSONArray events = jsonObject.getJSONArray("events");
for(int i = 0; i < events.length(); i++){
offset++;
JSONObject o = events.getJSONObject(i);
eventItems.add(new EventItem(o.getString("text"), o.getString("type"), o.getLong("time")));
}
}
catch (Exception e){
Dialogs.fehlermeldung("Fehler beim Laden der Ereignisse", eventView.findViewById(R.id.frame));
}
}
else{
Dialogs.fehlermeldung("Es konnten keine Ereignisse geladen werden", eventView.findViewById(R.id.frame));
}
if(firstLoad){
if(eventItems.size() > 0){
eventAdapter = new EventAdapter();
eventArray.setAdapter(eventAdapter);
}
else{
eventArray.setVisibility(View.GONE);
eventView.findViewById(R.id.empty_item).setVisibility(View.VISIBLE);
((ImageView) eventView.findViewById(R.id.empty_icon)).setImageResource(Icons.getDrawerIcon("events"));
((TextView) eventView.findViewById(R.id.empty_title)).setText("Keine Ereignisse");
((TextView) eventView.findViewById(R.id.empty_info)).setText("Es liegen keine Ereignisse vor.");
}
}
else{
if(result.equals("{\"events\":[]}")) endOfItems = true;
loading = false;
eventAdapter.notifyDataSetChanged();
}
}
@Override
public void onError(String msg) {
loading = false;
loadingAnimation.setVisibility(View.GONE);
eventView.findViewById(R.id.empty_item).setVisibility(View.VISIBLE);
((ImageView) eventView.findViewById(R.id.empty_icon)).setImageResource(Icons.getDrawerIcon("events"));
((TextView) eventView.findViewById(R.id.empty_title)).setText("Keine Ereignisse");
((TextView) eventView.findViewById(R.id.empty_info)).setText("Es konnten keine Ereignisse geladen werden.");
}
});
}
public class EventAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
final int VIEW_TYPE_EVENT = 0;
final int VIEW_TYPE_LOADING = 1;
int lastPosition = -1;
@Override
public int getItemCount(){
return eventItems.size();
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int i){
if(holder instanceof EventViewHolder){
final EventItem ei = eventItems.get(i);
final EventViewHolder eventViewHolder = (EventViewHolder) holder;
eventViewHolder.event.setText(ei.getText());
eventViewHolder.type.setText(ei.getType());
eventViewHolder.time.setText(ei.getFormattedTime());
setAnimation(((EventViewHolder) holder).container, i);
}
}
/**
* Startet die Animation auf der View
* @param viewToAnimate View
* @param position Index der View
*/
private void setAnimation(View viewToAnimate, int position){
if(position > lastPosition){
lastPosition = position;
Animation animation = AnimationUtils.loadAnimation(getContext(), R.anim.recycler_animation);
viewToAnimate.startAnimation(animation);
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType){
View itemView;
switch (viewType){
case VIEW_TYPE_EVENT:
itemView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.event_item, viewGroup, false);
return new EventViewHolder(itemView);
case VIEW_TYPE_LOADING:
itemView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.loading_row, viewGroup, false);
return new LoadingViewHolder(itemView);
}
return null;
}
@Override
public int getItemViewType(int position){
return eventItems.get(position) == null ? VIEW_TYPE_LOADING : VIEW_TYPE_EVENT;
}
public class EventViewHolder extends RecyclerView.ViewHolder{
protected TextView event, time, type;
protected View container;
public EventViewHolder(View v){
super(v);
container = v.findViewById(R.id.container);
event = (TextView) v.findViewById(R.id.event);
time = (TextView) v.findViewById(R.id.time);
type = (TextView) v.findViewById(R.id.type);
}
}
public class LoadingViewHolder extends RecyclerView.ViewHolder{
public LoadingViewHolder(View v){
super(v);
}
}
}
Layout-Dateien für Listenelemente erstellen
Als nächstes werden die Layouts "event_item.xml" und "loading_row.xml" erstellt. Dazu klickst du mit rechts auf eine Layout-Datei und wählst "New -> Layout resource file". Gib dem Layout den namen "event_item" und klicke auf "OK". Ändere im Layout als erstes den LinearLayout-Tag in ein RelativeLaoyout-Tag und entferne das Attribut "orientation". Füge stattdessen das Attribut "id" mit dem Wert "@+id/container" hinzu Außerdem änderst du den Wert des Attributs "layout_height" von "match_parent" auf "wrap_content". In den RelativeLayout-Tag fügst du eine CardView mit LinearLayouts und TextViews ein, die die Informationen anzeigen:<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardElevation="@dimen/cardViewElevation"
app:cardCornerRadius="@dimen/cardViewCornerRadius"
app:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="10dp"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium"
android:id="@+id/type"
android:textStyle="bold"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="10dp"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium"
android:id="@+id/time"
android:textStyle="bold"/>
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="10dp"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium"
android:id="@+id/event"
android:textStyle="bold"/>
</LinearLayout>
</android.support.v7.widget.CardView>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="7dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="75dp"
android:layout_margin="5dp"
android:layout_gravity="center">
<ProgressBar
android:layout_width="match_parent"
android:layout_height="match_parent"
android:indeterminate="true"/>
</RelativeLayout>
</LinearLayout>
final static String EXTRA_TYPE = "EXTRA_TYPE";
//Zu ladende Listenelemente
public final static int LOAD_ON_START = 10;
public final static int LOAD_ON_SCROLL = 5;
<item
android:id="@+id/action_event_type"
android:orderInCategory="100"
android:title="Ereignis-Typ wählen"
android:visible="false"
app:showAsAction="never" />
MainActivity anpassen
Anschließend wird in der MainActivity über der onCreate()-Methode neue eine Variable namens "optionsMenu" vom Typ "Menu" erstellt. In der Methode onCreateOptionsMenu() wird die Variable "optionsMenu" mit dem Wert des Methodenparameters "menu" initialisiert, indem du in die Methode die folgende Zeile einfügst:optionsMenu = menu;
else if(post_id == R.id.action_event_type){
loadEventTypes();
}
//Optionsitems setzen (wenn optionsMenu != null)
if(optionsMenu != null){
if(drawerItemList.get(position).getFragment() instanceof EventFragment){
optionsMenu.findItem(R.id.action_event_type).setVisible(true);
}
else{
optionsMenu.findItem(R.id.action_event_type).setVisible(false);
}
}
drawerItemList.add(new DrawerItem("Ereignisse", Icons.getDrawerIcon("events"), "events", new EventFragment()));
//Zur Abfrage der Ereignisanzahl
int eventsIndex;
if(!drawerItemList.isEmpty()){
eventsIndex = drawerItemList.size()-1;
loadEventCount(eventsIndex);
}
else{
Dialogs.fehlermeldung("Anzahl der ungelesenen Ereignisse kann nicht geladen werden.", findViewById(R.id.frame));
}
Dialogs.fehlermeldung("Fehler beim Laden der Räume", findViewById(R.id.frame));
/**
* Fragt die Anzahl der ungelesenen Ereignisse vom Server ab und aktualisiert das ensprechende Drawer-Item
* @param indexOfEventDrawerItem Index des Drawer-Items
*/
public void loadEventCount(final int indexOfEventDrawerItem){
Map<String, String> requestData = new HashMap<>();
requestData.put("action", "getunseenevents");
requestData.put("username", SaveData.getUsername(getApplicationContext()));
requestData.put("password", SaveData.getPassword(getApplicationContext()));
HTTPRequest.sendRequest(getApplicationContext(), requestData, SaveData.getServerIp(getApplicationContext()), new HTTPRequest.HTTPRequestCallback() {
@Override
public void onRequestResult(String result) {
switch(result){
default:
try{
int eventCount = Integer.parseInt(result);
if(eventCount > 0){
MenuItem item = drawerMenu.getItem(indexOfEventDrawerItem);
String title = "Ereignisse ("+eventCount+")";
item.setTitle(title);
drawerItemList.get(indexOfEventDrawerItem).name = title;
}
}
catch (Exception e){
e.printStackTrace();
}
break;
}
}
@Override
public void onError(String msg) {
Dialogs.fehlermeldung("Anzahl der ungelesenen Ereignisse kann nicht geladen werden.", findViewById(R.id.frame));
}
});
}
/**
* Erstellt einen Dialog, um ein Item auszuwählen
* @param title Titel des Dialogs
* @param cancelButtonText Titel des "Abbrechen"-Buttons
* @param context Kontext der App
* @param items Zur Wahl stehende Items
* @param onOk Listener für Item-Klicks
* @param onCancel Listener für Cancel-Klicks
*/
public static void singleChoiceDialog(String title, String cancelButtonText, Context context,
String[] items, final DialogInterface.OnClickListener onOk, final DialogInterface.OnClickListener onCancel){
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(title);
builder.setItems(items, onOk);
builder.setNegativeButton(cancelButtonText, onCancel);
builder.show();
}
/**
* Lädt alle Ereignis-Typen vom Server
*/
public void loadEventTypes(){
final Map<String, String> requestData = new HashMap<>();
requestData.put("action", "geteventtypes");
requestData.put("username", SaveData.getUsername(getApplicationContext()));
requestData.put("password", SaveData.getPassword(getApplicationContext()));
HTTPRequest.sendRequest(getApplicationContext(), requestData, SaveData.getServerIp(getApplicationContext()), new HTTPRequest.HTTPRequestCallback() {
@Override
public void onRequestResult(String result) {
switch (result){
default:
try{
JSONObject types = new JSONObject(result);
JSONArray eventTypes = types.getJSONArray("types");
final String[] items = new String[eventTypes.length()];
for(int i = 0; i < eventTypes.length(); i++){
items[i] = eventTypes.getString(i);
}
Dialogs.singleChoiceDialog("Ereignistypen wählen", "Abbrechen", MainActivity.this, items, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(MainActivity.this, EventActivity.class);
intent.putExtra(MainActivity.EXTRA_TYPE, items[which]);
startActivity(intent);
dialog.dismiss();
}
}, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
}
catch (Exception e){
e.printStackTrace();
Dialogs.fehlermeldung("Ereignistypen konnten nicht geladen werden", findViewById(R.id.frame));
}
break;
}
}
@Override
public void onError(String msg) {
}
});
}
EventActivity implementieren
Da die EventActivity noch nicht existiert, wird sie nun erstellt. Klicke dazu mit rechts auf eine Java-Datei und wähle "New -> Activity -> Empty Activity". Gib der Activity den Namen "EventActivity" und klicke auf "Finish". Zuerst wird das Layout der EventActivity angepasst. Dazu öffnest du die Datei "activity_event.xml" und änderst den ConstraintLayout-Tag in einen FrameLayout-Tag. Außerdem fügst du dem Tag das Attribut "id" mit dem Wert "@+id/frame" hinzu. Jetzt sollte der Code derLayout-Datei so aussehen:<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/frame"
tools:context="de.smarthome_blogger.smarthome.EventActivity">
</FrameLayout>
<item
android:id="@+id/action_event_type"
android:orderInCategory="100"
android:title="Ereignis-Typ wählen"
app:showAsAction="always"/>
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
//Intent abfangen
Intent intent = getIntent();
if(intent != null && intent.hasExtra(MainActivity.EXTRA_TYPE)){
type = intent.getStringExtra(MainActivity.EXTRA_TYPE);
}
setTitle(type);
//EventFragment einsetzen
Bundle bundle = new Bundle();
bundle.putString(MainActivity.EXTRA_TYPE, type);
Fragment eventFragment = new EventFragment();
eventFragment.setArguments(bundle);
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frame, eventFragment);
fragmentTransaction.commit();
@Override
public boolean onSupportNavigateUp(){
super.onSupportNavigateUp();
onBackPressed();
getSupportFragmentManager().popBackStack();
return true;
}
@Override
public boolean onCreateOptionsMenu(Menu menu){
getMenuInflater().inflate(R.menu.menu_event, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item){
switch(item.getItemId()){
case R.id.action_event_type:
loadEventTypes();
break;
}
return super.onOptionsItemSelected(item);
}
/**
* Lädt alle Ereignis-Typen vom Server
*/
public void loadEventTypes(){
final Map<String, String> requestData = new HashMap<>();
requestData.put("action", "geteventtypes");
requestData.put("username", SaveData.getUsername(getApplicationContext()));
requestData.put("password", SaveData.getPassword(getApplicationContext()));
HTTPRequest.sendRequest(getApplicationContext(), requestData, SaveData.getServerIp(getApplicationContext()), new HTTPRequest.HTTPRequestCallback() {
@Override
public void onRequestResult(String result) {
switch (result){
default:
try{
JSONObject types = new JSONObject(result);
JSONArray eventTypes = types.getJSONArray("types");
final String[] items = new String[eventTypes.length()];
for(int i = 0; i < eventTypes.length(); i++){
items[i] = eventTypes.getString(i);
}
Dialogs.singleChoiceDialog("Ereignistypen wählen", "Abbrechen", MainActivity.this, items, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(MainActivity.this, EventActivity.class);
intent.putExtra(MainActivity.EXTRA_TYPE, items[which]);
startActivity(intent);
dialog.dismiss();
}
}, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
}
catch (Exception e){
e.printStackTrace();
Dialogs.fehlermeldung("Ereignistypen konnten nicht geladen werden", findViewById(R.id.frame));
}
break;
}
}
@Override
public void onError(String msg) {
}
});
}
Dieser Beitrag hat dir gefallen?
Dann abonniere doch unseren Newsletter!