[WIP] Image loading and new UI for ItemActivity (Old DetailActivity)

This commit is contained in:
ChronosX88 2019-02-05 15:07:04 +04:00
parent 0a52ef6b42
commit efd2ad054b
No known key found for this signature in database
GPG Key ID: 8F92E090A87804AA
10 changed files with 287 additions and 156 deletions

Binary file not shown.

View File

@ -33,4 +33,5 @@ dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.4.0' implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0' implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'com.android.support:recyclerview-v7:27.1.1' implementation 'com.android.support:recyclerview-v7:27.1.1'
implementation 'com.squareup.picasso:picasso:2.71828'
} }

View File

@ -0,0 +1,92 @@
package ru.volgorobot.vrcatalog;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.AsyncTask;
import android.provider.MediaStore;
import android.util.Base64;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import ru.volgorobot.vrcatalog.additional.NetworkErrorException;
import ru.volgorobot.vrcatalog.additional.ResultWithErrorCode;
import ru.volgorobot.vrcatalog.model.CoreModel;
import ru.volgorobot.vrcatalog.model.ImageItemModel;
public class ItemPresenter implements MainContract.ItemPresenter {
// TODO
private MainContract.ItemView mView;
private Context context;
private MainContract.MainModel coreModel;
public ItemPresenter(Context context, MainContract.ItemView itemView) {
this.mView = itemView;
this.context = context;
this.coreModel = new CoreModel(context);
}
@SuppressLint("StaticFieldLeak")
@Override
public void getImagesByItemID(int id) {
new AsyncTask<Void, Void, ResultWithErrorCode<ArrayList<ImageItemModel>>>() {
@Override
protected ResultWithErrorCode<ArrayList<ImageItemModel>> doInBackground(Void... voids) {
ArrayList<ImageItemModel> images = null;
try {
images = coreModel.fetchImagesByItemID(id);
} catch (NetworkErrorException networkErrorException) {
return new ResultWithErrorCode<>(images, networkErrorException.getErrorCode());
} catch (NullPointerException nullPointerException) {
return new ResultWithErrorCode<>(images, 3);
}
return new ResultWithErrorCode<>(images, 0);
}
@Override
protected void onPostExecute(ResultWithErrorCode<ArrayList<ImageItemModel>> result) {
super.onPostExecute(result);
switch (result.getErrorCode()) {
case 0: {
//mView.setViewPagerContent(result.getData());
ArrayList<Bitmap> bitmaps = new ArrayList<>();
ArrayList<String> uriStrings = new ArrayList<>();
for (ImageItemModel imageItem : result.getData()) {
byte[] imageStream = Base64.decode(imageItem.getImage(), Base64.DEFAULT);
bitmaps.add(BitmapFactory.decodeByteArray(imageStream, 0, imageStream.length));
}
for (Bitmap bitmap : bitmaps) {
uriStrings.add(processImages(bitmap).toString());
}
// TODO: setViewPagerContent()
break;
}
case 1: {
mView.onFailureAnswer(1);
break;
}
case 2: {
mView.onFailureAnswer(2);
break;
}
case 3: {
mView.onFailureAnswer(3);
break;
}
}
}
}.execute();
}
private Uri processImages(Bitmap bitmap) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
String path = MediaStore.Images.Media.insertImage(context.getContentResolver(), bitmap, "Title", null);
return Uri.parse(path);
}
}

View File

@ -1,6 +1,7 @@
package ru.volgorobot.vrcatalog; package ru.volgorobot.vrcatalog;
import android.content.Intent; import android.content.Intent;
import android.net.Uri;
import java.util.ArrayList; import java.util.ArrayList;
@ -9,6 +10,7 @@ import ru.volgorobot.vrcatalog.additional.NetworkErrorException;
import ru.volgorobot.vrcatalog.model.CoreModel; import ru.volgorobot.vrcatalog.model.CoreModel;
import ru.volgorobot.vrcatalog.model.DetailModel; import ru.volgorobot.vrcatalog.model.DetailModel;
import ru.volgorobot.vrcatalog.model.FirstLevelModel; import ru.volgorobot.vrcatalog.model.FirstLevelModel;
import ru.volgorobot.vrcatalog.model.ImageItemModel;
import ru.volgorobot.vrcatalog.model.SecondLevelModel; import ru.volgorobot.vrcatalog.model.SecondLevelModel;
import ru.volgorobot.vrcatalog.model.ThirdLevelModel; import ru.volgorobot.vrcatalog.model.ThirdLevelModel;
@ -42,10 +44,11 @@ public interface MainContract {
ArrayList<ThirdLevelModel> fetchThirdLevelByParentID(int parentID) throws NetworkErrorException, NullPointerException; ArrayList<ThirdLevelModel> fetchThirdLevelByParentID(int parentID) throws NetworkErrorException, NullPointerException;
ArrayList<DetailModel> fetchDetailByID(int detailID) throws NetworkErrorException, NullPointerException; ArrayList<DetailModel> fetchDetailByID(int detailID) throws NetworkErrorException, NullPointerException;
void reinitializeApi() throws IllegalStateException; void reinitializeApi() throws IllegalStateException;
boolean getApiState(); boolean isApiNotNull();
ArrayList<ThirdLevelModel> fetchDetailsByName(String name) throws NetworkErrorException, NullPointerException; ArrayList<ThirdLevelModel> fetchDetailsByName(String name) throws NetworkErrorException, NullPointerException;
ArrayList<FirstLevelModel> fetchCategoryByID(int id) throws NetworkErrorException, NullPointerException; ArrayList<FirstLevelModel> fetchCategoryByID(int id) throws NetworkErrorException, NullPointerException;
ArrayList<SecondLevelModel> fetchSubCategoryByID(int id) throws NetworkErrorException, NullPointerException; ArrayList<SecondLevelModel> fetchSubCategoryByID(int id) throws NetworkErrorException, NullPointerException;
ArrayList<ImageItemModel> fetchImagesByItemID(int id) throws NetworkErrorException, NullPointerException;
} }
interface SearchablePresenter { interface SearchablePresenter {
@ -57,4 +60,13 @@ public interface MainContract {
void onFailureAnswer(int errorCode); void onFailureAnswer(int errorCode);
void swipeLayoutSetRefreshing(boolean state); void swipeLayoutSetRefreshing(boolean state);
} }
interface ItemPresenter {
void getImagesByItemID(int id);
}
interface ItemView {
void onFailureAnswer(int errorCode);
void setViewPagerContent(Uri uri);
}
} }

View File

@ -0,0 +1,58 @@
package ru.volgorobot.vrcatalog.additional;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.squareup.picasso.Picasso;
public class ImagePagerAdapter extends PagerAdapter {
/**
* This class is designed to control the ViewPager (images of item) in the activity of viewing details of items.
*/
private Context context;
private String[] imageUris;
public ImagePagerAdapter(Context context, String[] imageUris) {
this.context = context;
this.imageUris = imageUris;
}
@Override
public int getCount() {
return imageUris.length;
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}
/**
* This function initializes a new image from a URI.
* @return ImageView
*/
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
ImageView imageView = new ImageView(context);
Picasso.get()
.load(imageUris[position])
.fit()
.centerCrop()
.into(imageView);
container.addView(imageView);
return imageView;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
}

View File

@ -8,6 +8,7 @@ import retrofit2.http.Path;
import retrofit2.http.Query; import retrofit2.http.Query;
import ru.volgorobot.vrcatalog.model.DetailModel; import ru.volgorobot.vrcatalog.model.DetailModel;
import ru.volgorobot.vrcatalog.model.FirstLevelModel; import ru.volgorobot.vrcatalog.model.FirstLevelModel;
import ru.volgorobot.vrcatalog.model.ImageItemModel;
import ru.volgorobot.vrcatalog.model.SecondLevelModel; import ru.volgorobot.vrcatalog.model.SecondLevelModel;
import ru.volgorobot.vrcatalog.model.ThirdLevelModel; import ru.volgorobot.vrcatalog.model.ThirdLevelModel;
@ -33,4 +34,7 @@ public interface VRApi {
@GET("/api/item") @GET("/api/item")
Call<List<ThirdLevelModel>> getDetailsByName(@Query("name") String name); Call<List<ThirdLevelModel>> getDetailsByName(@Query("name") String name);
@GET("/api/item/{id}/images")
Call<List<ImageItemModel>> getImagesByItemID(@Path("id") int itemID);
} }

View File

@ -32,7 +32,7 @@ public class CoreModel implements MainContract.MainModel {
@Override @Override
public ArrayList<FirstLevelModel> fetchFirstLevel() throws NetworkErrorException, NullPointerException { public ArrayList<FirstLevelModel> fetchFirstLevel() throws NetworkErrorException, NullPointerException {
if(vrApi == null) { if(!isApiNotNull()) {
throw new NullPointerException(); throw new NullPointerException();
} }
final ArrayList<FirstLevelModel> firstLevelModels = new ArrayList<>(); final ArrayList<FirstLevelModel> firstLevelModels = new ArrayList<>();
@ -51,7 +51,7 @@ public class CoreModel implements MainContract.MainModel {
@Override @Override
public ArrayList<SecondLevelModel> fetchSecondLevelByParentID(int parentID) throws NetworkErrorException, NullPointerException { public ArrayList<SecondLevelModel> fetchSecondLevelByParentID(int parentID) throws NetworkErrorException, NullPointerException {
if(vrApi == null) { if(!isApiNotNull()) {
throw new NullPointerException(); throw new NullPointerException();
} }
final ArrayList<SecondLevelModel> secondLevelModels = new ArrayList<>(); final ArrayList<SecondLevelModel> secondLevelModels = new ArrayList<>();
@ -70,7 +70,7 @@ public class CoreModel implements MainContract.MainModel {
@Override @Override
public ArrayList<ThirdLevelModel> fetchThirdLevelByParentID(int parentID) throws NetworkErrorException, NullPointerException { public ArrayList<ThirdLevelModel> fetchThirdLevelByParentID(int parentID) throws NetworkErrorException, NullPointerException {
if(vrApi == null) { if(!isApiNotNull()) {
throw new NullPointerException(); throw new NullPointerException();
} }
final ArrayList<ThirdLevelModel> thirdLevelModels = new ArrayList<>(); final ArrayList<ThirdLevelModel> thirdLevelModels = new ArrayList<>();
@ -89,7 +89,7 @@ public class CoreModel implements MainContract.MainModel {
@Override @Override
public ArrayList<DetailModel> fetchDetailByID(int detailID) throws NetworkErrorException, NullPointerException { public ArrayList<DetailModel> fetchDetailByID(int detailID) throws NetworkErrorException, NullPointerException {
if(vrApi == null) { if(!isApiNotNull()) {
throw new NullPointerException(); throw new NullPointerException();
} }
ArrayList<DetailModel> detail = new ArrayList<>(); ArrayList<DetailModel> detail = new ArrayList<>();
@ -118,17 +118,13 @@ public class CoreModel implements MainContract.MainModel {
} }
@Override @Override
public boolean getApiState() { public boolean isApiNotNull() {
if(vrApi != null) { return vrApi != null;
return true;
} else {
return false;
}
} }
@Override @Override
public ArrayList<ThirdLevelModel> fetchDetailsByName(String name) throws NetworkErrorException, NullPointerException { public ArrayList<ThirdLevelModel> fetchDetailsByName(String name) throws NetworkErrorException, NullPointerException {
if(vrApi == null) { if(!isApiNotNull()) {
throw new NullPointerException(); throw new NullPointerException();
} }
final ArrayList<ThirdLevelModel> thirdLevelModels = new ArrayList<>(); final ArrayList<ThirdLevelModel> thirdLevelModels = new ArrayList<>();
@ -147,7 +143,7 @@ public class CoreModel implements MainContract.MainModel {
@Override @Override
public ArrayList<FirstLevelModel> fetchCategoryByID(int id) throws NetworkErrorException, NullPointerException { public ArrayList<FirstLevelModel> fetchCategoryByID(int id) throws NetworkErrorException, NullPointerException {
if(vrApi == null) { if(!isApiNotNull()) {
throw new NullPointerException(); throw new NullPointerException();
} }
final ArrayList<FirstLevelModel> firstLevelModels = new ArrayList<>(); final ArrayList<FirstLevelModel> firstLevelModels = new ArrayList<>();
@ -166,7 +162,7 @@ public class CoreModel implements MainContract.MainModel {
@Override @Override
public ArrayList<SecondLevelModel> fetchSubCategoryByID(int id) throws NetworkErrorException, NullPointerException { public ArrayList<SecondLevelModel> fetchSubCategoryByID(int id) throws NetworkErrorException, NullPointerException {
if(vrApi == null) { if(!isApiNotNull()) {
throw new NullPointerException(); throw new NullPointerException();
} }
final ArrayList<SecondLevelModel> secondLevelModels = new ArrayList<>(); final ArrayList<SecondLevelModel> secondLevelModels = new ArrayList<>();
@ -182,4 +178,23 @@ public class CoreModel implements MainContract.MainModel {
} }
return secondLevelModels; return secondLevelModels;
} }
@Override
public ArrayList<ImageItemModel> fetchImagesByItemID(int id) throws NetworkErrorException, NullPointerException {
if(!isApiNotNull()) {
throw new NullPointerException();
}
final ArrayList<ImageItemModel> imageItemModels = new ArrayList<>();
try {
Response<List<ImageItemModel>> response = vrApi.getImagesByItemID(id).execute();
if(response.isSuccessful()) {
imageItemModels.addAll(response.body());
} else {
throw new NetworkErrorException(2);
}
} catch (IOException e) {
throw new NetworkErrorException(1);
}
return imageItemModels;
}
} }

View File

@ -0,0 +1,40 @@
package ru.volgorobot.vrcatalog.model;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class ImageItemModel {
@SerializedName("DetailID")
@Expose
private Integer detailID;
@SerializedName("image")
@Expose
private String image;
@SerializedName("MD5")
@Expose
private String MD5;
public Integer getDetailID() {
return detailID;
}
public void setDetailID(Integer detailID) {
this.detailID = detailID;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public String getMD5() {
return MD5;
}
public void setMD5(String mD5) {
this.MD5 = mD5;
}
}

View File

@ -1,18 +1,23 @@
package ru.volgorobot.vrcatalog.view; package ru.volgorobot.vrcatalog.view;
import android.content.Intent; import android.content.Intent;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.view.MenuItem; import android.view.MenuItem;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import java.util.HashMap; import java.util.HashMap;
import ru.volgorobot.vrcatalog.ItemPresenter;
import ru.volgorobot.vrcatalog.MainContract;
import ru.volgorobot.vrcatalog.R; import ru.volgorobot.vrcatalog.R;
public class DetailActivity extends AppCompatActivity { public class DetailActivity extends AppCompatActivity implements MainContract.ItemView {
TextView nameView; TextView nameView;
TextView quantityView; TextView quantityView;
TextView priceView; TextView priceView;
@ -20,6 +25,7 @@ public class DetailActivity extends AppCompatActivity {
TextView analogueView; TextView analogueView;
TextView datasheetView; TextView datasheetView;
EditText notesView; EditText notesView;
private MainContract.ItemPresenter mPresenter;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -28,6 +34,7 @@ public class DetailActivity extends AppCompatActivity {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true); getSupportActionBar().setHomeButtonEnabled(true);
initializeViews(); initializeViews();
mPresenter = new ItemPresenter(DetailActivity.this, this);
Intent intent = getIntent(); Intent intent = getIntent();
@ -110,4 +117,30 @@ public class DetailActivity extends AppCompatActivity {
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
@Override
public void onFailureAnswer(int errorCode) {
switch (errorCode) {
case 1: {
Toast.makeText(DetailActivity.this, "Ошибка сети. Проверьте подключение к сети или данные подключения к API!", Toast.LENGTH_LONG).show();
Log.e("DetailActivity", "Network Error! Re-check your connection credentials or network settings!");
break;
}
case 2: {
Toast.makeText(DetailActivity.this, "Ответ от сервера неверен! Перепроверьте данные подключения!", Toast.LENGTH_LONG).show();
Log.e("DetailActivity", "Answer of server is wrong! Re-check your connection credentials!");
break;
}
case 3: {
Toast.makeText(DetailActivity.this, "Вы ввели неверный URL. Пример: http://example.ru", Toast.LENGTH_LONG).show();
Log.e("DetailActivity", "Invalid-formatted URL. Please, check URL (change his if it need) and try again.");
break;
}
}
}
@Override
public void setViewPagerContent(Uri uri) {
}
} }

View File

@ -1,155 +1,31 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:layout_editor_absoluteY="81dp"> android:orientation="vertical">
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="wrap_content"
android:layout_height="313dp" />
<TextView <TextView
android:id="@+id/name_text" android:id="@+id/itemName"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="40dp"
android:layout_marginStart="16dp" android:text="Item Name"
android:text="Название: "
android:textAppearance="@style/TextAppearance.AppCompat.Body1" android:textAppearance="@style/TextAppearance.AppCompat.Body1"
app:layout_constraintBaseline_toBaselineOf="@+id/name" android:textSize="30sp" />
app:layout_constraintStart_toStartOf="parent" />
<TextView <TextView
android:id="@+id/name" android:id="@+id/itemPrice"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:text="Item Price"
android:text="TextView"
app:layout_constraintStart_toEndOf="@+id/name_text"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/notes_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="172dp"
android:text="Описание: "
android:textAppearance="@style/TextAppearance.AppCompat.Body1" android:textAppearance="@style/TextAppearance.AppCompat.Body1"
app:layout_constraintStart_toStartOf="@+id/notes" android:textSize="24sp" />
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/quantity_text"
android:layout_width="wrap_content"
android:layout_height="19dp"
android:text="Количество на складе: "
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
app:layout_constraintBaseline_toBaselineOf="@+id/quantity"
app:layout_constraintStart_toStartOf="@+id/name_text" />
<TextView </LinearLayout>
android:id="@+id/quantity"
android:layout_width="wrap_content"
android:layout_height="19dp"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="@+id/price"
app:layout_constraintStart_toEndOf="@+id/quantity_text"
app:layout_constraintTop_toTopOf="@+id/name" />
<EditText
android:id="@+id/notes"
android:layout_width="0dp"
android:layout_height="99dp"
android:layout_marginStart="16dp"
android:layout_marginTop="18dp"
android:layout_marginEnd="16dp"
android:ems="10"
android:focusable="false"
android:focusableInTouchMode="false"
android:inputType="textMultiLine"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/notes_text">
<requestFocus />
</EditText>
<TextView
android:id="@+id/price_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Цена: "
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
app:layout_constraintBaseline_toBaselineOf="@+id/price"
app:layout_constraintStart_toStartOf="@+id/quantity_text" />
<TextView
android:id="@+id/price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="11dp"
android:text="TextView"
app:layout_constraintBottom_toTopOf="@+id/country_text"
app:layout_constraintStart_toEndOf="@+id/price_text" />
<TextView
android:id="@+id/country_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="Страна изготовления: "
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
app:layout_constraintBaseline_toBaselineOf="@+id/country"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/country"
android:layout_width="wrap_content"
android:layout_height="19dp"
android:layout_marginTop="86dp"
android:layout_marginBottom="86dp"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="@+id/notes_text"
app:layout_constraintStart_toEndOf="@+id/country_text"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/analogue_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="Аналоги: "
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
app:layout_constraintBaseline_toBaselineOf="@+id/analogue"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/analogue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="9dp"
android:text="TextView"
app:layout_constraintStart_toEndOf="@+id/analogue_text"
app:layout_constraintTop_toBottomOf="@+id/country_text" />
<TextView
android:id="@+id/datasheet_text"
android:layout_width="wrap_content"
android:layout_height="19dp"
android:layout_marginStart="16dp"
android:text="Техническая спецификация: "
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
app:layout_constraintBaseline_toBaselineOf="@+id/datasheet"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/datasheet"
android:layout_width="wrap_content"
android:layout_height="18dp"
android:layout_marginTop="128dp"
android:layout_marginBottom="127dp"
android:autoLink="web"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="@+id/notes"
app:layout_constraintStart_toEndOf="@+id/datasheet_text"
app:layout_constraintTop_toTopOf="@+id/name" />
</android.support.constraint.ConstraintLayout>