[Tutorial Fortgeschrittene] Verarbeitung und Kommunikation von MySQL-Datenbank mit Square Libraries

  • Antworten:10
Ludy
  • Admin
  • Forum-Beiträge: 7.958

19.10.2016, 20:11:21 via Website

Einleitung

Inspiriert von Pascals Tutorial Android an eine PHP Rest Api anbinden, stelle ich euch eine weitere Datenverarbeitung der ausgelesenen Daten vor - Select, Insert, Update, Delete.

Um das Tutorial nicht zu sprengen, verzichte ich auf eine komplette PHP-API und stell sie verkürzt da, schließlich soll es hier um die Verarbeitung mit einer der mächtigsten Libraries Ersteller Square gehen.
Ich werde hier folgende Libraries (auszugsweise für das Tutorial gebrauchte) nutzen:

Square
- OkHttp
- Gson Converter
- Retrofit

Google & Android
- Design Support Library
- v7 recyclerview library
- v7 cardview library

(!) PHP-Injections und Layout werden vernachlässigt (!)
(!) achtet auf die Namespace, sie werden abweichend sein - zu eurem erstellten Projekt (!)


Was wird benötigt?


Übersicht

— geändert am 21.10.2016, 09:03:15

Gruß Ludy (App Entwickler)

Mein Beitrag hat dir geholfen? Lass doch ein "Danke" da.☺

☕ Buy Me A Coffee ☕

Lebensmittelwarnung-App

✨Meine Wunschliste✨

📲Telegram NextPit News📲

pepperonasPascal P.

Antworten
Ludy
  • Admin
  • Forum-Beiträge: 7.958

19.10.2016, 20:31:43 via Website

Schritt 1 - Layouts

Erstellt euch ein neues Projekt und wählt Basic Activity, das erleichtert uns schon etwas - das Layout wird uns erstellt - und wir brauchen nicht ganz so viel schreiben ;-)
Wenn AndroidStudio soweit ist, kümmern wir uns als erstes um die compile Importe.

{Projekt-Name}/app/build.gradle

compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support:cardview-v7:25.3.1'
compile 'com.android.support:recyclerview-v7:25.3.1'
compile 'com.android.support:design:25.3.1'
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.squareup.okhttp3:okhttp:3.8.1'

Ergänzt in der AndroidMainfest.xml der Berechtigung:

<uses-permission android:name="android.permission.INTERNET"/>

In dem von AS erstelltem Layout content_main.xml, müssen wir unser RecyclerView hinzufügen.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:id="@+id/content_main2"
    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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="org.astra_g.mysqldatabaseapi.MainActivity"
    android:orientation="horizontal"
    tools:showIn="@layout/activity_main">
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical"/>
</LinearLayout>

Ein Layout müssen wir noch erstellen card_view_row.xml, wir verwenden unser eigenes CardView-Layout.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="8dp"
    android:paddingRight="8dp">

    <android.support.v7.widget.CardView
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:card_view="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:cardUseCompatPadding="true"
        card_view:cardCornerRadius="8dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_margin="8dp"
            android:orientation="vertical">

            <TextView
                android:id="@+id/textViewId"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="id"
                android:textAppearance="?android:attr/textAppearanceSmall"/>

            <TextView
                android:id="@+id/textViewManufacturer"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Manufacturer"
                android:textAppearance="?android:attr/textAppearanceMedium"/>

            <TextView
                android:id="@+id/textViewType"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Type"
                android:textAppearance="?android:attr/textAppearanceMedium"/>
        </LinearLayout>
    </android.support.v7.widget.CardView>
</LinearLayout>

— geändert am 05.07.2017, 10:00:45

Gruß Ludy (App Entwickler)

Mein Beitrag hat dir geholfen? Lass doch ein "Danke" da.☺

☕ Buy Me A Coffee ☕

Lebensmittelwarnung-App

✨Meine Wunschliste✨

📲Telegram NextPit News📲

pepperonasPascal P.

Antworten
Ludy
  • Admin
  • Forum-Beiträge: 7.958

19.10.2016, 22:22:13 via Website

Schritt 2 - Activity, Helferklassen, Interface

Bevor wir zum Kern kommen, brauchen wir eine Struktur, da wir mit Json bzw. Gson arbeiten.
Wir haben später ein Array (ArrayList<>), welches Devices heißt und Details.

Schematisch würde das so aussehen:

Devices
    |_____id_____manufacturer_____type
    |_____id_____manufacturer_____type
    |_____id_____manufacturer_____type

Die Model-Klasse nenne ich sinnvollerweise Devices.java in der wir auch gleich die Details-Klasse einbetten.

Devices.java

public class Devices {
    @SerializedName("devices")
    private ArrayList<Details> details;

    public ArrayList<Details> getDetails() {
        return details;
    }

    public class Details {
        private String id;
        private String manufacturer;
        private String type;

        public String getId() {
            return id;
        }

        public String getManufacturer() {
            return manufacturer;
        }

        public String getType() {
            return type;
        }
    }
}

Für Retrofit benötigen wir ein Interface - ApiService.java - zum empfangen der Daten nutzen wir an dieser Stelle GET als Methode. In der Klammer von @GET ist der Pfad zur zu triggernden Datei, dem Call wird die Klasse Devices übergeben.

ApiService.java

public interface ApiService {

    String BASE_URL = "http://192.168.188.20:8080";

    @GET("/api/devices.php")
    Call<Devices> getDevices();
}

Das Interface wir für die einzelnen App-Funktionen nach und nach erweitert.


In der MainActivity.java müssen wir unsern RecyclerView initialisieren, die Toolbar sollte von AS schon initialisiert sein.
Nun erstellen wir uns eine Variable vom Type:

private Retrofit restApi;
private ApiService urlApiService;
private Call<Devices> mDevicesCall;
private ArrayList<Devices.Details> details;

restApi wird über den Builder die BASE_URL übergeben und angewiesen Gson zu konvergieren.
apiService wird von restApi erstellt, es wird ein Proxy-Service herstellt und die ApiService.class übergeben.
mDevicesCall wird der definierte Call von apiService über geben getDevices() und mit enqueue ein Callback von Devices eingebettet. Darin wir der Response bzw. der Fail zur Weiterverarbeitung implementiert.

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private RecyclerView mRecyclerView;
    private Retrofit restApi;
    private ApiService apiService;
    private Call<Devices> mDevicesCall;
    private ArrayList<Devices.Details> details;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        mRecyclerView.setHasFixedSize(true);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        restApi = new Retrofit.Builder()
                .baseUrl(ApiService.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        apiService = restApi.create(ApiService.class);
        mDevicesCall = apiService.getDevices();
        mDevicesCall.enqueue(new Callback<Devices>() {
            @Override
            public void onResponse(Call<Devices> call, Response<Devices> response) {
                details = response.body().getDetails();
                for (Devices.Details d : details) {
                    Log.d("TAG", d.getId());
                }
            }

            @Override
            public void onFailure(Call<Devices> call, Throwable t) {
                 Log.d("TAG", t.getLocalizedMessage());
            }
        });
    }
}

— geändert am 21.10.2016, 07:51:11

Gruß Ludy (App Entwickler)

Mein Beitrag hat dir geholfen? Lass doch ein "Danke" da.☺

☕ Buy Me A Coffee ☕

Lebensmittelwarnung-App

✨Meine Wunschliste✨

📲Telegram NextPit News📲

pepperonasPascal P.

Antworten
Ludy
  • Admin
  • Forum-Beiträge: 7.958

19.10.2016, 23:56:31 via Website

Schritt 3 - Create DB & SELECT Devices Tabelle

Datenbank erstellen mit:

CREATE TABLE `devices` ( `_id` INT NOT NULL AUTO_INCREMENT , `manufacturer` VARCHAR(50) NOT NULL , `type` VARCHAR(50) NOT NULL , PRIMARY KEY (`_id`));
INSERT INTO `devices` (`manufacturer`, `type`) VALUES ('Motorola', 'Moto X Play '), ('LG', 'G3');

devices.php

<?php
    include 'dbconn.php'; // wird im Tutorial nicht abgehandelt
    header('Content-Type: application/json');
    $response = array();
    $query = "SELECT * FROM `devices`"; // Tabellen devices 
    $conn->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $result = $conn->db->query($query);
    $response['devices'] = array();
    while ($row = $result->fetch(PDO::FETCH_BOTH)) {
        $data = array();
        $data['id'] = $row['_id'];
        $data['manufacturer'] = $row['manufacturer'];
        $data['type'] = $row['type'];
        array_push($response['devices'], $data);
    }
    echo json_encode($response, JSON_PRETTY_PRINT);
    $result->closeCursor();
    $conn->db = null;
?>

Wenn nun die App gestartet wird, werden in der Log-Ausgabe nach und nach die ID-Nummer der Datenbank bzw. Tabelle ausgegeben.

— geändert am 20.10.2016, 20:40:35

Gruß Ludy (App Entwickler)

Mein Beitrag hat dir geholfen? Lass doch ein "Danke" da.☺

☕ Buy Me A Coffee ☕

Lebensmittelwarnung-App

✨Meine Wunschliste✨

📲Telegram NextPit News📲

pepperonasPascal P.

Antworten
Ludy
  • Admin
  • Forum-Beiträge: 7.958

20.10.2016, 09:04:37 via Website

Schritt 4 - RecyclerView.Adapter

Nun wollen wir in der App auch was sehen und nicht nur im Log, also bauen wir uns erstmal einen Adapter (DeviceAdapter.java), der das RecyclerView füllt und in einzelne CardView darstellt. Die Klasse wird erweitert mit:

RecyclerView.Adapter<DeviceAdapter.ViewHolder>

Unserem Constructor übergeben wir die ArrayList Details und erstellen eine Variable mDetails.

public class DeviceAdapter extends RecyclerView.Adapter<DeviceAdapter.ViewHolder> {

    private ArrayList<Devices.Details> mDetails;

    public DeviceAdapter(ArrayList<Devices.Details> details) {
        mDetails = details;
    }

In der onCreateViewHolder wird das Layout card_view_row.xml inflated dem DeviceAdapter.ViewHolder bei der Erstellung mitgegeben.

@Override
public DeviceAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.card_view_row, parent, false);
    return new ViewHolder(view);
}

Die onBindViewHolder ist die Methode, in der aus der ArrayListe von mDetails, die einzelnen Item's ausgelesen werden und je einer CardView zugewiesen wird. Da es eine ArrayList ist, können wir bequem über die Position des Views, die Position des Item bzw. des Inhalt abfragen.

@Override
public void onBindViewHolder(DeviceAdapter.ViewHolder holder, int position) {
    Devices.Details details = mDetails.get(position);
    holder.textViewId.setText(details.getId());
    holder.textViewManufacturer.setText(details.getManufacturer());
    holder.textViewType.setText(details.getType());
}

Übergabe der Anzahl der Einträge in dem ArrayList.

@Override
public int getItemCount() {
    return mDetails.size();
}

Im ViewHolder initialisieren wir die einzelnen Views (TextView)

class ViewHolder extends RecyclerView.ViewHolder {

    private TextView textViewId;
    private TextView textViewManufacturer;
    private TextView textViewType;

    ViewHolder(View itemView) {
        super(itemView);
        this.textViewId = (TextView) itemView.findViewById(R.id.textViewId);
        this.textViewManufacturer = (TextView) itemView.findViewById(R.id.textViewManufacturer);
        this.textViewType = (TextView) itemView.findViewById(R.id.textViewType);
    }
}

DeviceAdapter.java

public class DeviceAdapter extends RecyclerView.Adapter<DeviceAdapter.ViewHolder> {

    private ArrayList<Devices.Details> mDetails;

    public DeviceAdapter(ArrayList<Devices.Details> details) {
        mDetails = details;
    }

    @Override
    public DeviceAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.card_view_row, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(DeviceAdapter.ViewHolder holder, int position) {
        Devices.Details details = mDetails.get(position);
        holder.textViewId.setText(details.getId());
        holder.textViewManufacturer.setText(details.getManufacturer());
        holder.textViewType.setText(details.getType());
    }

    @Override
    public int getItemCount() {
        return mDetails.size();
    }

    class ViewHolder extends RecyclerView.ViewHolder {

        private TextView textViewId;
        private TextView textViewManufacturer;
        private TextView textViewType;

        ViewHolder(View itemView) {
            super(itemView);
            this.textViewId = (TextView) itemView.findViewById(R.id.textViewId);
            this.textViewManufacturer = (TextView) itemView.findViewById(R.id.textViewManufacturer);
            this.textViewType = (TextView) itemView.findViewById(R.id.textViewType);
        }
    }
}

Der Adapter ist nun fertig zum Benutzen, dazu in die MainActivity im Response von mDevicesCall und den Adapter erstellen und die ArrayList#Details übergeben. Den erstellten Adapter übergeben wir nun dem RecyclerView.

adapter = new DeviceAdapter(details);
mRecyclerView.setAdapter(adapter);

— geändert am 21.10.2016, 07:54:47

Gruß Ludy (App Entwickler)

Mein Beitrag hat dir geholfen? Lass doch ein "Danke" da.☺

☕ Buy Me A Coffee ☕

Lebensmittelwarnung-App

✨Meine Wunschliste✨

📲Telegram NextPit News📲

pepperonasPascal P.

Antworten
Ludy
  • Admin
  • Forum-Beiträge: 7.958

20.10.2016, 12:12:01 via Website

Schritt 5 - Insert AlertDialog

In der insert.php nutzen wir zum Empfangen, die POST-Methode, das wird noch wichtig für die Ausführung der Insert-Methode in der App.

insert.php

<?php
    include 'dbconn.php';
    header('Content-Type: application/json');
    $response = array();

    $manufacturer = (isset($_POST['manufacturer'], $_POST['type'])) ? $_POST['manufacturer'] : die(json_encode(array('success' => -1), JSON_PRETTY_PRINT));
    $type = (isset($_POST['manufacturer'], $_POST['type'])) ? $_POST['type'] : die(json_encode(array('success' => -1), JSON_PRETTY_PRINT));

    $insert = "INSERT INTO devices(manufacturer, type) VALUES(:manufacturer, :type)";
    $insertQuery = $conn->db->prepare($insert);
    $insertQuery->bindParam(":manufacturer", $manufacturer, PDO:: PARAM_STR);
    $insertQuery->bindParam(":type", $type, PDO:: PARAM_STR);

    if ($insertQuery->execute() && $insertQuery->errorCode() == 0) {
        echo json_encode(array('success' => 1), JSON_PRETTY_PRINT);
    } else {
        echo json_encode(array('success' => -1), JSON_PRETTY_PRINT);
    }

    $insertQuery->closeCursor();
    $conn->db = null;
?>

Wir benötigen noch eine Klasse Insert, welche für den Callback (Response) unseres ApiService gebraucht wird.

public class Insert {
    private int success;

    public int getSuccess() {
        return success;
    }
}

Zunächst erweitern wir das Interface ApiService.
@FormUrlEncoded - dadurch werden die POST-Field's encoded.
@POST - hier wird wieder der Pfad zur Datei angegeben und gleichzeitig auf die POST-Methode verwiesen
@Field - die Felder, die im PHP-Script als POST's verarbeitet werden sollen.

@FormUrlEncoded
@POST("/api/insert.php")
Call<Insert> insertDevice(
        @Field("manufacturer")
        String manufacturer,
        @Field("type")
        String type
);

Wir erstellen uns ein eigenes Layout, da wir zwei EditText-Views brauchen.

prompts.xml

<?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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">

    <EditText
        android:id="@+id/editTextDialogManufacturer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Manufacturer">
        <requestFocus/>
    </EditText>

    <EditText
        android:id="@+id/editTextDialogType"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Type"/>
</LinearLayout>

Nun erstellen wir uns direkt eine Methode für den AlertDialog, um noch etwas Übersicht zu haben. Dazu inflate wir das Layout prompts.xml und initialisieren die EditText-Views.
In onClick des Positiv-Button werden dir die Daten aus den EditText-Views an den Server senden. Wie beim Select aus der Datenbank funktioniert das Ganze auch beim Insert zur Datenbank.

Call insertCall = apiService.insertDevice(manufacturer, type);`

Das Resultat - ob es geklappt hat oder nicht - lassen wir uns in einem Toast anzeigen.

private void showDialogInsert(Context context) {
    LayoutInflater li = LayoutInflater.from(context);
    View promptsView = li.inflate(R.layout.prompts, null);

    AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
            context);
    alertDialogBuilder.setTitle("Device added");
    alertDialogBuilder.setView(promptsView);

    final EditText userInputManufacturer = (EditText) promptsView
            .findViewById(R.id.editTextDialogManufacturer);
    final EditText userInputType = (EditText) promptsView
            .findViewById(R.id.editTextDialogType);

    alertDialogBuilder
            .setCancelable(false)
            .setPositiveButton(getString(android.R.string.ok),
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            String manufacturer = userInputManufacturer.getText().toString();
                            String type = userInputType.getText().toString();
                            Call<Insert> insertCall = apiService.insertDevice(manufacturer, type);
                            insertCall.enqueue(new Callback<Insert>() {
                                @Override
                                public void onResponse(Call<Insert> call, Response<Insert> response) {
                                    switch (response.body().getSuccess()) {
                                        case -1:
                                            Toast.makeText(getApplicationContext(), "etwas ist schief gelaufen.", Toast.LENGTH_LONG).show();
                                            break;
                                        case 1:
                                            Toast.makeText(getApplicationContext(), "Alle OK.", Toast.LENGTH_LONG).show();
                                            break;
                                    }
                                }

                                @Override
                                public void onFailure(Call<Insert> call, Throwable t) {
                                    Log.e("Fail", t.getLocalizedMessage());
                                }
                            });
                        }
                    })
            .setNegativeButton(getString(android.R.string.cancel),
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            dialog.cancel();
                        }
                    });
    AlertDialog alertDialog = alertDialogBuilder.create();
    alertDialog.show();
}

Aufrufen werden wir das über einen Menüpunkt, dazu brauchen wir eine menu.xml - erstellt euch die Datei mit "Android recource file".

menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu
    xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/action_add"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:icon="@android:drawable/ic_input_add"
        android:title="Add"
        app:showAsAction="always"/>
</menu>

In der MainActivity.java fügen wir die OptionMenu-Methoden ein und können nun die showDialogInsert-Methode aufrufen.

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.action_add:
            showDialogInsert(MainActivity.this);
            return true;
    }
    return super.onOptionsItemSelected(item);
}

— geändert am 20.10.2016, 20:45:04

Gruß Ludy (App Entwickler)

Mein Beitrag hat dir geholfen? Lass doch ein "Danke" da.☺

☕ Buy Me A Coffee ☕

Lebensmittelwarnung-App

✨Meine Wunschliste✨

📲Telegram NextPit News📲

pepperonasPascal P.

Antworten
Ludy
  • Admin
  • Forum-Beiträge: 7.958

20.10.2016, 13:13:10 via Website

Schritt 6 - ItemClickSupport RecyclerView

Aus einer ListView sind wir gewohnt, den Eintrag an zu klicken und eine Aktion wird ausgeführt, beim RecyclerView gibt es nicht den OnItemClickListener und OnItemLongClickListener.
Aber die Jungs von Little Robots haben eine Hilfsklasse vorgestellt und die werden wir uns zu Eigen machen.

ItemClickSupport.java

/**
 * http://www.littlerobots.nl/blog/Handle-Android-RecyclerView-Clicks/
 */
public class ItemClickSupport {
    private final RecyclerView mRecyclerView;
    private OnItemClickListener mOnItemClickListener;
    private OnItemLongClickListener mOnItemLongClickListener;
    private View.OnClickListener mOnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (mOnItemClickListener != null) {
                RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
                mOnItemClickListener.onItemClicked(mRecyclerView, holder.getAdapterPosition(), v);
            }
        }
    };
    private View.OnLongClickListener mOnLongClickListener = new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
            if (mOnItemLongClickListener != null) {
                RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
                return mOnItemLongClickListener.onItemLongClicked(mRecyclerView, holder.getAdapterPosition(), v);
            }
            return false;
        }
    };
    private RecyclerView.OnChildAttachStateChangeListener mAttachListener
            = new RecyclerView.OnChildAttachStateChangeListener() {
        @Override
        public void onChildViewAttachedToWindow(View view) {
            if (mOnItemClickListener != null) {
                view.setOnClickListener(mOnClickListener);
            }
            if (mOnItemLongClickListener != null) {
                view.setOnLongClickListener(mOnLongClickListener);
            }
        }

        @Override
        public void onChildViewDetachedFromWindow(View view) {

        }
    };

    private ItemClickSupport(RecyclerView recyclerView) {
        mRecyclerView = recyclerView;
        mRecyclerView.setTag(R.id.item_click_support, this);
        mRecyclerView.addOnChildAttachStateChangeListener(mAttachListener);
    }

    public static ItemClickSupport addTo(RecyclerView view) {
        ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
        if (support == null) {
            support = new ItemClickSupport(view);
        }
        return support;
    }

    public static ItemClickSupport removeFrom(RecyclerView view) {
        ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
        if (support != null) {
            support.detach(view);
        }
        return support;
    }

    public ItemClickSupport setOnItemClickListener(OnItemClickListener listener) {
        mOnItemClickListener = listener;
        return this;
    }

    public ItemClickSupport setOnItemLongClickListener(OnItemLongClickListener listener) {
        mOnItemLongClickListener = listener;
        return this;
    }

    private void detach(RecyclerView view) {
        view.removeOnChildAttachStateChangeListener(mAttachListener);
        view.setTag(R.id.item_click_support, null);
    }

    public interface OnItemClickListener {

        void onItemClicked(RecyclerView recyclerView, int position, View v);
    }

    public interface OnItemLongClickListener {

        boolean onItemLongClicked(RecyclerView recyclerView, int position, View v);
    }
}

In values brauchen wir noch die ids.xml.

ids.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="item_click_support" type="id"/>
</resources>

— geändert am 20.10.2016, 13:23:47

Gruß Ludy (App Entwickler)

Mein Beitrag hat dir geholfen? Lass doch ein "Danke" da.☺

☕ Buy Me A Coffee ☕

Lebensmittelwarnung-App

✨Meine Wunschliste✨

📲Telegram NextPit News📲

pepperonasPascal P.

Antworten
Ludy
  • Admin
  • Forum-Beiträge: 7.958

20.10.2016, 16:39:12 via Website

Schritt 7 - Update AlertDialog

Wie beim Insert-AlertDialog, nutzen wir hier fast die gleiche Syntax.

update.php

<?php
    include 'dbconn.php';
    header('Content-Type: application/json');
    $response = array();

    $id = (isset($_POST['id'], $_POST['manufacturer'], $_POST['type'])) ? $_POST['id'] : die(json_encode(array('success' => -1), JSON_PRETTY_PRINT));
    $manufacturer = (isset($_POST['id'], $_POST['manufacturer'], $_POST['type'])) ? $_POST['manufacturer'] : die(json_encode(array('success' => -1), JSON_PRETTY_PRINT));
    $type = (isset($_POST['id'], $_POST['manufacturer'], $_POST['type'])) ? $_POST['type'] : die(json_encode(array('success' => -1), JSON_PRETTY_PRINT));

    $update = "UPDATE devices SET manufacturer = :manufacturer, type = :type WHERE _id = :id";
    $updateQuery = $conn->db->prepare($update);
    $updateQuery->bindParam(":manufacturer", $manufacturer, PDO:: PARAM_STR);
    $updateQuery->bindParam(":type", $type, PDO:: PARAM_STR);
    $updateQuery->bindParam(":id", $id, PDO:: PARAM_INT);

    if ($updateQuery->execute() && $updateQuery->errorCode() == 0) {
        echo json_encode(array('success' => 1), JSON_PRETTY_PRINT);
    } else {
        echo json_encode(array('success' => -1), JSON_PRETTY_PRINT);
    }

    $updateQuery->closeCursor();
    $conn->db = null;
?>

Wir benötigen noch eine Klasse Update, welche für den Callback (Response) unseres ApiService gebraucht wird.

Update.java

public class Update {
    private int success;

    public int getSuccess() {
        return success;
    }
}

Zunächst erweitern wir wieder das Interface ApiService.

@FormUrlEncoded
@POST("/api/update.php")
Call<Update> updateDevice(
    @Field("id")
    String id,
    @Field("manufacturer")
    String manufacturer,
    @Field("type")
    String type
);

Das prompts.xml-Layout können wir gleich weiter nutzen. Und nutzen werden wir natürlich den Positiv-Button um die Methode showDialogUpdate aufzurufen.

private void showDialogUpdate(Context context, final String _id) {
    LayoutInflater li = LayoutInflater.from(context);
    View promptsView = li.inflate(R.layout.prompts, null);

    AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
            context);
    alertDialogBuilder.setTitle("Device update");
    alertDialogBuilder.setView(promptsView);

    final EditText userInputManufacturer = (EditText) promptsView
            .findViewById(R.id.editTextDialogManufacturer);
    final EditText userInputType = (EditText) promptsView
            .findViewById(R.id.editTextDialogType);

    alertDialogBuilder
            .setCancelable(false)
            .setPositiveButton(getString(android.R.string.ok),
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            String manufacturer = userInputManufacturer.getText().toString();
                            String type = userInputType.getText().toString();
                            Call<Update> insertCall = apiService.updateDevice(_id, manufacturer, type);
                            insertCall.enqueue(new Callback<Update>() {
                                @Override
                                public void onResponse(Call<Update> call, Response<Update> response) {
                                    switch (response.body().getSuccess()) {
                                        case -1:
                                            Toast.makeText(getApplicationContext(), "etwas ist schief gelaufen.", Toast.LENGTH_LONG).show();
                                            break;
                                        case 1:
                                            Toast.makeText(getApplicationContext(), "Alle OK.", Toast.LENGTH_LONG).show();
                                            break;
                                    }
                                }

                                @Override
                                public void onFailure(Call<Update> call, Throwable t) {
                                    Log.e("Fail", t.getLocalizedMessage());
                                }
                            });
                        }
                    })
            .setNegativeButton(getString(android.R.string.cancel),
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            dialog.cancel();
                        }
                    });
    AlertDialog alertDialog = alertDialogBuilder.create();
    alertDialog.show();
}

Um den AlertDialog aufzurufen, werden wir die mit der ItemClickSupport-Klasse die OnItemLongClick-Methode nutzen. Diese setzten wir sinnvollerweiser am Ende der onCreate-Methode.

ItemClickSupport.addTo(mRecyclerView).setOnItemLongClickListener(new ItemClickSupport.OnItemLongClickListener() {
    @Override
    public boolean onItemLongClicked(RecyclerView recyclerView, int position, View v) {
        showDialogUpdate(MainActivity.this, details.get(position).getId());
        return true;
    }
});

— geändert am 20.10.2016, 20:53:38

Gruß Ludy (App Entwickler)

Mein Beitrag hat dir geholfen? Lass doch ein "Danke" da.☺

☕ Buy Me A Coffee ☕

Lebensmittelwarnung-App

✨Meine Wunschliste✨

📲Telegram NextPit News📲

pepperonasPascal P.

Antworten
Ludy
  • Admin
  • Forum-Beiträge: 7.958

20.10.2016, 18:27:35 via Website

Schritt 8 - Delete Swip Left/Right

delete.php

<?php
    include 'dbconn.php';

    $response = array();
    $id = (isset($_POST['id'])) ? $_POST['id'] : die(json_encode(array('success' => -1), JSON_PRETTY_PRINT));
    $delete = "DELETE FROM devices WHERE _id =  :id";
    $deleteQuery = $conn->db->prepare($delete);

    $deleteQuery->bindParam(':id', $id, PDO:: PARAM_INT);   

    if ($deleteQuery->execute() && ($deleteQuery->rowCount() > 0) && $deleteQuery->errorCode() == 0) {
        echo json_encode(array('success' => 1), JSON_PRETTY_PRINT);
    } else {
        echo json_encode(array('success' => -1), JSON_PRETTY_PRINT);
    }
    $deleteQuery->closeCursor();
    $conn->db = null
?>

Eine Helfer-Klasse für das Löschen benötigen wir.

Delete.java

public class Delete {
    private int success;

    public int getSuccess() {
        return success;
    }
}

Wer hätte es gedacht - wir müssen unsere ApiService.java anpassen bzw. erweitern.

ApiService.java

@FormUrlEncoded
@POST("/api/delete.php")
Call<Delete> deleteDevice(
        @Field("id")
        String id
);

Es muss unsere Adapter-Klasse erweitert werden, benötigt wird dazu noch ein Callback-Interface und eine Methode - onItemRemove.

ItemRemoveCallback.java

public interface ItemRemoveCallback {
    void get();
}

In DeviceAdapter legen wir eine Variable von ItemRemoveCallback an.

DeviceAdapter.java

private ItemRemoveCallback itemRemoveCallback;

public void onItemRemove(RecyclerView.ViewHolder viewHolder, final RecyclerView mRecyclerView, final ItemRemoveCallback itemRemoveCallback) {
    this.itemRemoveCallback = itemRemoveCallback;
    final int adapterPosition = viewHolder.getAdapterPosition();
    final Devices.Details details = mDetails.get(adapterPosition);
    Snackbar snackbar = Snackbar.make(mRecyclerView, "removed", Snackbar.LENGTH_LONG)
            .setAction("undo", new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    mDetails.add(adapterPosition, details);
                    notifyItemInserted(adapterPosition);
                    mRecyclerView.scrollToPosition(adapterPosition);
                }
            })
            .setCallback(new Snackbar.Callback() {
                @Override
                public void onDismissed(Snackbar snackbar, int event) {
                    super.onDismissed(snackbar, event);
                    if (event != DISMISS_EVENT_ACTION) {
                        itemRemoveCallback.get();
                    }
                }
            });
    snackbar.show();
    mDetails.remove(adapterPosition);
    notifyItemRemoved(adapterPosition);
}

MainActivity.java

private ItemTouchHelper.SimpleCallback simpleCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        return false;
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        final String _id = details.get(viewHolder.getAdapterPosition()).getId();
        adapter.onItemRemove(viewHolder, mRecyclerView, new ItemRemoveCallback() {

            @Override
            public void get() {

            }
        });
    }
};

In der onCreate müssen wir simpleCallback dem ItemTouchHelper übergeben und anschließend an den RecyclerView "anhängen".

ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleCallback);
itemTouchHelper.attachToRecyclerView(mRecyclerView);

In get() von onItemRemove handeln wir den Löschvorgang in der Datenbank ab.

@Override
public void get() {
    Call<Delete> deleteCall = apiService.deleteDevice(_id);
    deleteCall.enqueue(new Callback<Delete>() {
        @Override
        public void onResponse(Call<Delete> call, Response<Delete> response) {
            if (response.body().getSuccess() != -1) {
                Toast.makeText(getApplicationContext(), "ok", Toast.LENGTH_LONG).show();
            }
        }

        @Override
        public void onFailure(Call<Delete> call, Throwable t) {
            Log.e("TAG", t.getLocalizedMessage());
        }
    });
}

— geändert am 20.10.2016, 20:59:48

Gruß Ludy (App Entwickler)

Mein Beitrag hat dir geholfen? Lass doch ein "Danke" da.☺

☕ Buy Me A Coffee ☕

Lebensmittelwarnung-App

✨Meine Wunschliste✨

📲Telegram NextPit News📲

pepperonasPascal P.

Antworten
Ludy
  • Admin
  • Forum-Beiträge: 7.958

20.10.2016, 21:07:58 via Website

Schlusswort

Enthaltene Fehler werden nach dem auffinden beseitigt.
Alle hier aufgezeigten Libraries unterliegen ihren jeweiligen Lizenzen.
Weder AndroidPIT noch ich übernehmen Haftung bei beschädigen der Soft- und/oder Hardware.
Ich habe das Tutorial nach besten Wissen und Gewissen (nicht vorhanden :D) geschrieben.
Ich hoffe es ist einigermaßen verständlich geschrieben, wobei das für die fortgeschrittenen User sollte das selbst erklärend sein ;-).

Das vollständige Projekt findet ihr hier.

— geändert am 06.12.2016, 19:21:34

Gruß Ludy (App Entwickler)

Mein Beitrag hat dir geholfen? Lass doch ein "Danke" da.☺

☕ Buy Me A Coffee ☕

Lebensmittelwarnung-App

✨Meine Wunschliste✨

📲Telegram NextPit News📲

pepperonasPascal P.swa00

Antworten