EX69
퍼미션하기
android:usesCleartextTraffic="true"
<uses-permission android:name="android.permission.INTERNET"/>
gradle>viewbinding하기
buildFeatures {
viewBinding true
// 5개하면 됨. 시작부터
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'
implementation 'com.github.bumptech.glide:glide:4.16.0'
//새로고침기능XML에서 사용
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01'
//
05Retrofit> insetDB.php
<?php
header('Content-Type:text/plain; charset=utf-8');
//@PartMap으로 전달된 POST방식의 데이터들
$name= $_POST['name'];
$title= $_POST['title'];
$message=$_POST['msg'];
$price=$_POST['price'];
//@Part로 전달된 이미지파일
$file= $_FILES['img'];
$srcName= $file['name']; //원본파일명
$tmpName=$file['tmp_name']; //임시저장소 위치
$dstName="./image/IMG_" . date('YmdHis') .$srcName;
move_uploaded_file($tmpName,$dstName);
//데이터가 잘 왔는지 확인.
// echo $name . "<br>";
// echo $title . "<br>";
// echo $message . "<br>";
// echo $price . "<br>";
// echo $tmpName . "<br>";
// echo $dstName . "<br>";
$now= date('Y-m-d H:i');
//메세지중에서 특수문자 사용가능성 있음.
$message= addslashes($message);
$title=addslashes($title);
//MySQL DB에 데이터들 저장 [테이블명: market]
$db =mysqli_connect("localhost","l**8","q1*****r4!","le****8");
mysqli_query($db,"set names utf8");
//$dstName 파일 저장한 마지막 위치
//데이터들[$name,$title,$message,$price,$dstName,$now] insert
$sql="INSERT INTO market(name, title, msg, price, file, date) VALUES('$name','$title','$message','$price','$dstName','$now')";
$result= mysqli_query($db,$sql);
if($result) echo "게시글이 업로드 되었습니다.";
else echo "게시글 업로드에 실패했습니다. 다시 시도해 주세요.";
mysqli_close($db);
?>
loadDB.php
<?php
header('Content-Type:application/json; charset=utf-8');
$db= mysqli_connect("localhost","l****8","q*****r4!","le***8");
mysqli_query($db,"set names utf8");
$sql="SELECT * FROM market";
$resultSet= mysqli_query($db,$sql);
//결과표로부터 총 레코드(한줄:row) 수
$rowNum= mysqli_num_rows($resultSet);
//여러줄을 읽어야 하기에..ASSOC 연관배열
//빈배열에 한줄짜리
$rows= array();
for($i=0; $i<$rowNum; $i++){
$row= mysqli_fetch_array($resultSet, MYSQLI_ASSOC);
$rows[$i]= $row;
}
//2차원 배열 -->json array변환 후 에코
echo json_encode($rows);
mysqli_close($db);
?>
main.xml
플로팅버튼= 벡터에셋
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
tools:context=".MainActivity">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/tool"
android:layout_width="match_parent"
app:title="게시판"
android:background="#D715F8"
app:titleTextColor="@color/white"
app:titleCentered="true"
android:layout_height="wrap_content"/>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/refresh_layout"
android:layout_width="match_parent"
android:layout_below="@id/tool"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:orientation="vertical"/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/Widget.MaterialComponents.FloatingActionButton"
app:backgroundTint="#D715F8"
android:src="@drawable/baseline_border_color_11"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_margin="16dp"/>
</RelativeLayout>
edit.xml
arrowicon= image에셋
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
android:layout_height="match_parent"
tools:context=".EditActivity">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#D715F8"
app:title="글작성"
app:titleTextColor="@color/white"
app:titleCentered="true"
app:navigationIcon="@drawable/ic_action_arrow_back"
app:navigationIconTint="@color/white"/>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/input_layout_name"
android:layout_width="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="16dp"
android:layout_height="wrap_content"
android:hint="이름">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/input_layout_title"
android:layout_width="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="8dp"
android:layout_height="wrap_content"
android:hint="제목">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/input_layout_msg"
android:layout_width="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="8dp"
android:layout_height="wrap_content"
android:hint="상세 내용">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textMultiLine"
android:lines="5"
android:gravity="top"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/input_layout_price"
android:layout_width="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="8dp"
android:layout_height="wrap_content"
android:hint="가격">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"/>
</com.google.android.material.textfield.TextInputLayout>
<Button
android:id="@+id/btn_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="select image"
style="@style/Widget.MaterialComponents.Button"
android:layout_gravity="right"
android:layout_marginRight="16dp"
android:textStyle="bold"
android:backgroundTint="#D715F8"
android:layout_marginTop="8dp"/>
<ImageView
android:id="@+id/iv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginRight="16dp"
android:layout_marginLeft="16dp"/>
<Button
android:id="@+id/btn_complete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Widget.MaterialComponents.Button"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="8dp"
android:backgroundTint="#D715F8"
android:textStyle="bold"
android:text="작성완료"/>
</LinearLayout>
item.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="120dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:padding="8dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp">
<androidx.cardview.widget.CardView
android:id="@+id/cv"
android:layout_width="120dp"
android:layout_height="match_parent"
app:cardCornerRadius="8dp">
<ImageView
android:id="@+id/iv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/paris"/>
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="안드로이드 앱 개발"
android:textSize="16sp"
android:textColor="@color/black"
android:textStyle="bold"
android:layout_toRightOf="@id/cv"
android:layout_marginLeft="12dp"
android:maxLines="2"
android:ellipsize="end"/>
<TextView
android:id="@+id/tv_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="안드로이드 앱을 만들어 드립니다."
android:textSize="12sp"
android:textColor="#6C6B6B"
android:layout_alignLeft="@id/tv_title"
android:layout_below="@id/tv_title"
android:maxLines="3"
android:ellipsize="end"/>
<TextView
android:id="@+id/tv_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="500000원"
android:textSize="14sp"
android:textColor="#D715F8"
android:textStyle="bold"
android:layout_alignLeft="@id/tv_msg"
android:layout_alignParentBottom="true"/>
<androidx.appcompat.widget.AppCompatImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignRight="@id/tv_msg"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:backgroundTint="@color/white"
android:src="@drawable/baseline_star_24"/>
</RelativeLayout>
main.java
package com.eunji0118.ex69retrofitmarketapp;
import androidx.appcompat.app.AppCompatActivity;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
import com.eunji0118.ex69retrofitmarketapp.databinding.ActivityMainBinding;
import java.util.ArrayList;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
public class MainActivity extends AppCompatActivity {
ActivityMainBinding binding;
ArrayList<MarketItem> marketItems=new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding=ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.fab.setOnClickListener(view -> {
Intent intent=new Intent(this, EditActivity.class);
startActivity(intent);
});
//새로고침만들기
binding.refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
loadData();
binding.refreshLayout.setRefreshing(false); //로딩 아이콘 제거
}
});
}
@Override
protected void onResume() {
super.onResume();
loadData(); //서버에서 데이터 가져오기..
}
void loadData(){
Retrofit retrofit= RetrofitHelper.getRetrofitInstance();
RetrofitService retrofitService=retrofit.create(RetrofitService.class);
Call<ArrayList<MarketItem>> call =retrofitService.loadDataFromServer();
call.enqueue(new Callback<ArrayList<MarketItem>>() {
@Override
public void onResponse(Call<ArrayList<MarketItem>> call, Response<ArrayList<MarketItem>> response) {
marketItems.clear(); //기존의 것 없애고 다시
ArrayList<MarketItem> items=response.body();
//특정아이템 가져오기
for (MarketItem item: items){
marketItems.add(0,item);//0번이 최신으로 들어가게
}
MarketAdapter adapter=new MarketAdapter(MainActivity.this,marketItems);
binding.recyclerView.setAdapter(adapter);
}
@Override
public void onFailure(Call<ArrayList<MarketItem>> call, Throwable t) {
Toast.makeText(MainActivity.this, "error"+t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
}
edit.java
package com.eunji0118.ex69retrofitmarketapp;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import androidx.loader.content.CursorLoader;
import android.app.AlertDialog;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import com.eunji0118.ex69retrofitmarketapp.databinding.ActivityEditBinding;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
public class EditActivity extends AppCompatActivity {
ActivityEditBinding binding;
//3.여러개면 리스트로 만듦.
String imgPath;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding= ActivityEditBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.toolbar.setNavigationOnClickListener(view -> finish());
binding.btnSelect.setOnClickListener(view -> clickSelect());
binding.btnComplete.setOnClickListener(view -> clickComplete());
}
void clickSelect(){
Intent intent=new Intent(MediaStore.ACTION_PICK_IMAGES);
//2.
resultLauncher.launch(intent);
}
//1.
ActivityResultLauncher<Intent> resultLauncher=registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),result -> {
if (result.getResultCode()==RESULT_CANCELED) return;
Intent intent=result.getData();
Uri uri=intent.getData();
Glide.with(this).load(uri).into(binding.iv);
imgPath=getRealPathFromUri(uri);
//나오는거 확인
// new AlertDialog.Builder(this).setMessage(imgPath).create().show();
});
//복붙
//Uri -- > 절대경로로 바꿔서 리턴시켜주는 메소드
String getRealPathFromUri(Uri uri){
String[] proj= {MediaStore.Images.Media.DATA};
CursorLoader loader= new CursorLoader(this, uri, proj, null, null, null);
Cursor cursor= loader.loadInBackground();
int column_index= cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
String result= cursor.getString(column_index);
cursor.close();
return result;
}
void clickComplete(){
//작성한 데이터들 서버에 전송하기 (업로드)
//전송할 데이터들[name, title,message, price, imgPath]
String name=binding.inputLayoutName.getEditText().getText().toString();
String title=binding.inputLayoutTitle.getEditText().getText().toString();
String message=binding.inputLayoutMsg.getEditText().getText().toString();
String price=binding.inputLayoutPrice.getEditText().getText().toString();
//레트로핏 작업 5단계 헬퍼 만들고 ..
Retrofit retrofit=RetrofitHelper.getRetrofitInstance();
//명세서작성2,3
RetrofitService retrofitService= retrofit.create(RetrofitService.class);
//4.
//string 데이터들
Map<String,String> dataPart=new HashMap<>();
dataPart.put("name",name);
dataPart.put("title",title);
dataPart.put("msg",message);
dataPart.put("price",price);
//이미지 파일(순서대로 포장)
MultipartBody.Part filePart=null;
if (imgPath!=null){
File file=new File(imgPath);
RequestBody body= RequestBody.create(MediaType.parse("image/*"),file);
filePart=MultipartBody.Part.createFormData("img",file.getName(),body);
}
Call<String> call= retrofitService.postDataToServer(dataPart,filePart);
call.enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, Response<String> response) {
String s=response.body();
Toast.makeText(EditActivity.this, "응답: "+s, Toast.LENGTH_SHORT).show();
finish();
}
@Override
public void onFailure(Call<String> call, Throwable t) {
Toast.makeText(EditActivity.this, "error"+t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
}
marketAdapter.java
package com.eunji0118.ex69retrofitmarketapp;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.eunji0118.ex69retrofitmarketapp.databinding.RecyclerItemBinding;
import java.util.ArrayList;
public class MarketAdapter extends RecyclerView.Adapter<MarketAdapter.VH> {
Context context;
ArrayList<MarketItem> marketItems;
public MarketAdapter(Context context, ArrayList<MarketItem> marketItems) {
this.context = context;
this.marketItems = marketItems;
}
@NonNull
@Override
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView= LayoutInflater.from(context).inflate(R.layout.recycler_item,parent,false);
return new VH(itemView);
}
@Override
public void onBindViewHolder(@NonNull VH holder, int position) {
MarketItem item=marketItems.get(position);
holder.binding.tvTitle.setText(item.title);
holder.binding.tvMsg.setText(item.msg);
holder.binding.tvPrice.setText(item.price+"원");
//서버주소는 절대적으로 바꿔야함.
String url="http://lej0118.dothome.co.kr/05Retrofit/"+item.file;
Glide.with(context).load(url).into(holder.binding.iv);
}
@Override
public int getItemCount() {
return marketItems.size();
}
class VH extends RecyclerView.ViewHolder{
RecyclerItemBinding binding;
public VH(@NonNull View itemView) {
super(itemView);
binding=RecyclerItemBinding.bind(itemView);
}
}
}
marketItem.java
package com.eunji0118.ex69retrofitmarketapp;
public class MarketItem {
int no;
String name;
String title;
String msg;
String price;
String file;
String date;
public MarketItem(int no, String name, String title, String msg, String price, String file, String date) {
this.no = no;
this.name = name;
this.title = title;
this.msg = msg;
this.price = price;
this.file = file;
this.date = date;
}
public MarketItem() {
}
}
retrofithelper.java
package com.eunji0118.ex69retrofitmarketapp;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;
public class RetrofitHelper {
public static Retrofit getRetrofitInstance(){
Retrofit.Builder builder=new Retrofit.Builder();
builder.baseUrl("http://lej0118.dothome.co.kr");
//스칼라먼저 쓸 것.
builder.addConverterFactory(ScalarsConverterFactory.create());
builder.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit=builder.build();
return retrofit;
}
}
retrofitservice.interface
package com.eunji0118.ex69retrofitmarketapp;
import java.util.ArrayList;
import java.util.Map;
import okhttp3.MultipartBody;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
import retrofit2.http.PartMap;
public interface RetrofitService {
@Multipart//데이터 서버로 보내는 것.
@POST("05Retrofit/insertDB.php") //식별자, 값 (다른박스 사용.파일, 글씨)
Call<String> postDataToServer(@PartMap Map<String,String> dataPart,
@Part MultipartBody.Part filePart);
@GET("05Retrofit/loadDB.php")
Call<ArrayList<MarketItem>> loadDataFromServer();
}
'AndroidStudio' 카테고리의 다른 글
Firebase : google 클라우드 백엔드 플랫폼 *실시간 채팅창만들기 (0) | 2023.09.07 |
---|---|
Uri를 절대경로로 바꿔서 리턴시켜주는 메소드 (0) | 2023.09.06 |
웹 서비스 HTTP통신 : 이미지만 올리기* (1) | 2023.09.06 |
Google playMarket APP 등록 준비사항 (0) | 2023.08.31 |
Open API 데이터 표기형식 (0) | 2023.08.31 |