4월 1주차. Java Android 개발

변현섭·2024년 4월 2일
0

다우데이타 인턴십

목록 보기
12/17
post-thumbnail

1. RecyclerView 구현하기

1) RecyclerView 적용하기

① recylerview_item.xml 파일 만들기

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:background="@color/white"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="10dp">

    <ImageView
        android:id="@+id/iv_item_image"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:scaleType="fitCenter"
        android:src="@drawable/ic_image"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_item_path"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:paddingHorizontal="5dp"
        android:text="image path"
        android:textColor="@color/black"
        android:textSize="15sp"
        app:layout_constraintBottom_toBottomOf="@+id/iv_item_image"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/iv_item_image"
        app:layout_constraintTop_toTopOf="@+id/iv_item_image" />


</androidx.constraintlayout.widget.ConstraintLayout>

② RecyclerView를 적용할 xml 파일에 RecyclerView 태그 적용하기

<androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv_image_items"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:orientation="vertical"
            android:layout_marginVertical="10dp"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            app:layout_constraintBottom_toBottomOf="@+id/cardView"
            app:layout_constraintEnd_toEndOf="@+id/cardView"
            app:layout_constraintStart_toStartOf="@+id/cardView"
            app:layout_constraintTop_toTopOf="@+id/cardView"
            tools:listitem="@layout/rv_images" />

③ Data Model 생성하기

  • DataModel이라는 이름의 Java Class를 생성한 후 아래의 코드를 입력한다.
  • 여기서는 이미지의 URI와 경로 값을 저장하도록 하겠다.
public class DataModel {
    String imgUri;
    String imagePath;

    public String getImgUri() {
        return imgUri;
    }

    public void setImgUri(String imgUri) {
        this.imgUri = imgUri;
    }

    public String getImagePath() {
        return imagePath;
    }

    public void setImagePath(String imagePath) {
        this.imagePath = imagePath;
    }

    public DataModel(String imgUri, String imagePath) {
        this.imgUri = imgUri;
        this.imagePath = imagePath;
    }
}

④ Adapter 클래스 생성하기

  • ImageRVAdapter라는 이름의 Java Class를 생성한 후 아래의 코드를 입력한다.
public class ImageRVAdapter extends RecyclerView.Adapter<ImageRVAdapter.ViewHolder> {

    private ArrayList<DataModel> dataModels;

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

    @Override
    public void onBindViewHolder(@NonNull ImageRVAdapter.ViewHolder holder, int position) {
        holder.onBind(dataModels.get(position));
    }

    public void setdataModels(ArrayList<DataModel> dataModels){
        this.dataModels = dataModels;
        notifyDataSetChanged();
    }

    @Override
    public int getItemCount() {
        if(dataModels == null) return 0;
        return dataModels.size();
    }

    class ViewHolder extends RecyclerView.ViewHolder {
        ImageView img;
        TextView imgPath;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);

            img = (ImageView) itemView.findViewById(R.id.iv_item_image);
            imgPath = (TextView) itemView.findViewById(R.id.tv_item_path);
        }

        void onBind(DataModel dataModel){
            img.setImageURI(Uri.parse(dataModel.getImgUri()));
            imgPath.setText(dataModel.getImagePath());
        }
    }
}

⑤ List와 RV Adapter 연결하기

  • RecyclerView를 적용할 Activity에 아래의 코드를 입력한다.
public class MainActivity extends AppCompatActivity {
	ActivityMainBinding binding;
    private List<DataModel> dataModels = new ArrayList<>();
    ImageRVAdapter adapter;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

        RecyclerView recyclerView;
        recyclerView = binding.rvImageItems;
        adapter = new ImageRVAdapter();
        recyclerView.setAdapter(adapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        adapter.setdataModels((ArrayList<DataModel>) dataModels);
        ...

⑥ 더미데이터 생성하기

  • 아래와 같은 형식으로 더미데이터를 리스트에 추가할 수 있다.
DataModel dataModel = new DataModel(selectedImageURI, picturePath);
dataModels.add(dataModel);
adapter.setdataModels((ArrayList<DataModel>) dataModels);

2) 아이템 클릭 리스너 등록하기

① ImageRVAdapater에 아래의 내용을 추가한다.

public interface OnItemClickListener {
    void onItemClick(View v, int pos);
}

private OnItemClickListener onItemClickListener = null;

public void setOnItemClickListener(OnItemClickListener listener) {
    this.onItemClickListener = listener;
}

② ViewHolder의 생성자 안에 아래의 내용을 입력한다.

public ViewHolder(@NonNull View itemView) {
	super(itemView);

    img = (ImageView) itemView.findViewById(R.id.iv_item_image);
    imgPath = (TextView) itemView.findViewById(R.id.tv_item_path);
    deleteBtn = (ImageView) itemView.findViewById(R.id.iv_delete);

    img.setOnClickListener(new View.OnClickListener() {
    	@Override
        public void onClick(View v) {
        	int position = getAdapterPosition();
            if (position != RecyclerView.NO_POSITION) {
            	if (onItemClickListener != null) {
                	onItemClickListener.onItemClick(v, position);
                }
            }
         }
     });
     ...
        
	deleteBtn.setOnClickListener(new View.OnClickListener() {
		@Override
		public void onClick(View v) {
        	int position = getAdapterPosition();
        	if (position != RecyclerView.NO_POSITION && onItemClickListener != null) {
            	dataModels.remove(position);
            	notifyItemRemoved(position);
            	notifyItemRangeChanged(position, dataModels.size());
        	}
    	}
	});
    ...

③ RecyclerView를 적용할 Activity에서, setAdapter() 전에 아래의 내용을 입력한다.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
    
    RecyclerView recyclerView;
    recyclerView = binding.rvImageItems;
    adapter = new ImageRVAdapter();
    
    adapter.setOnItemClickListener(new ImageRVAdapter.OnItemClickListener() {
        @Override
        public void onItemClick(View v, int pos) {
            Intent intent = new Intent(MainActivity.this, DetailImageActivity.class);
            intent.putExtra("imgUri", dataModels.get(pos).getImgUri());
            startActivity(intent);
        }
    });
    
    recyclerView.setAdapter(adapter);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    adapter.setDataModels((ArrayList<DataModel>) dataModels);
    ...

3) Adapter 클래스에서 Activity의 UI 조작하기

업로드 한 이미지가 있을 때에는 업로드 아이콘이 안 나타나다가, 이미지 목록이 비워지면 다시 나타나게 하는 방법에 대해 알아보자.

① Activity에서 Adapter로 UI 변경을 요청하기 위해 아래와 같은 메서드를 Activity에 정의한다.

public void updateImageViewVisibility() {
    if (dataModels.size() == 0) {
        binding.ivImageUpload.setVisibility(View.VISIBLE);
    } else {
        binding.ivImageUpload.setVisibility(View.INVISIBLE);
    }
}

② Adapater 클래스의 삭제 버튼에 대한 클릭 이벤트 리스너의 아래의 내용을 추가한다.

  • 위에서 정의한 메서드를 Adapter에서 호출한다.
eleteBtn.setOnClickListener(new View.OnClickListener() {
	@Override
    public void onClick(View v) {
    	...
        ((MainActivity) v.getContext()).updateImageViewVisibility();

2. 권한 요청하기

갤러리 이미지에 접근하기 위해 필요한 권한을 요청하는 방법에 대해 알아보자.

① Manifest 파일에 아래의 내용을 추가한다.

<!-- 갤러리 권한 ~ver.10 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

<!-- 갤러리 권한 ver.11~ -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>

② 권한 부여를 요청하고자하는 Activity 파일에 아래의 내용을 입력한다.

public class MainActivity extends AppCompatActivity {

	@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    	...
        
        checkPermission();
    }
    ...
    
    private boolean checkPermission() {
        String permission;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            permission = READ_MEDIA_IMAGES;
        } else {
            permission = READ_EXTERNAL_STORAGE;
        }

        int result = ContextCompat.checkSelfPermission(getApplicationContext(), permission);
        if (result != PackageManager.PERMISSION_GRANTED) {
            Log.e(TAG, "Permission for accessing media is not granted: " + result);
            requestPermission();
        } else {
            Log.e(TAG, "Permission for accessing media is granted: " + result);
        }
        return result == PackageManager.PERMISSION_GRANTED;
    }
    
	private static final int PERMISSION_REQUEST_CODE = 200;
    
	private void requestPermission() {
        String permission;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            permission = READ_MEDIA_IMAGES;
        } else {
            permission = READ_EXTERNAL_STORAGE;
        }
        boolean shouldProvideRationale = shouldShowRequestPermissionRationale(permission);
        Log.e(TAG, "requestPermission. Should provide rationale: " + shouldProvideRationale);
        requestPermissions(new String[]{permission}, PERMISSION_REQUEST_CODE);
    }
    
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == PERMISSION_REQUEST_CODE) {
            // 사용자가 권한 요청에 대한 응답을 했을 때
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 권한이 허용된 경우
                Log.e(TAG, "Permission granted");
            } else if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_DENIED) {
                // 권한이 거부된 경우
                Log.e(TAG, "Permission denied");
                Toast.makeText(this, "설정 또는 팝업에서 접근 권한을 허용해주세요.", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

3. App Scheme

앱 스키마란, 안드로이드 앱을 식별하는 데 사용되는 고유한 식별자를 의미한다. 앱 스키마는 App to App 및 Web to App 호출에 사용될 수 있다.

1) App Scheme 등록하기

Manifest 파일에서 실행시키고자 하는 Activity 안에 아래의 내용을 입력한다.

<activity
    android:name=".MainActivity"
    android:exported="true">
    
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:host="action" android:scheme="test-app" />
    </intent-filter>
    
</activity>

이로써, 등록절차가 완료된다. Activity의 URI Scheme는 {shcheme}://{host}가 되므로, 여기서는 test-app://action이 App Scheme이 된다.

2) App Scheme을 이용한 App 호출

App Scheme 등록이 완료되었다면, App을 호출하기 위한 별도의 App을 제작해야 한다. 새로운 프로젝트를 생성한 후 아래의 코드를 입력하자. 이는 버튼을 클릭했을 때, App Scheme에 대응하는 앱이 실행되는 상황을 가정한다.

 btn.setOnClickListener {
	String uriScheme = "test-app://action";
	Intent intent = new Intent();
    intent.setAction(Intent.ACTION_VIEW);
    intent.setData(Uri.parse(uriScheme));
    startActivity(intent);
 }

이 때, 호출용 App과 App Scheme을 등록한 App 모두 디바이스에 설치되어 있어야 한다. 참고로, App Scheme을 등록한 이후에는, 오직 외부 호출을 통해서만 해당 앱을 실행할 수 있게 된다. 즉, 휴대폰에 설치는 되어있지만, 실물 App 형태로 존재하지는 않는 것이다.

4. Android Studio에서 APK 파일 추출하기

① Build > Generate Signed Bundle / APK 버튼을 클릭한다.

② APK를 선택하고 Next 버튼을 클릭한다.

③ Key Store Path를 새로 등록하기 위해 Create new 버튼을 클릭한다.

④ Key store path 우측의 폴더 버튼을 클릭한다.

⑤ Key 파일의 경로와 이름을 지정한 뒤, OK 버튼을 클릭한다.

⑥ Password를 입력하고 Certificate 요소 중 적어도 하나의 정보를 입력한다.

⑦ 상황에 따라 Remember passwords에 체크 또는 체크 해제한 후, Next 버튼을 클릭한다.

⑧ release 선택 후 Create 버튼을 클릭한다.

⑨ 아래와 같이 완료 팝업이 뜨면, locate 버튼을 클릭한다.

⑩ 추출된 apk 파일을 확인할 수 있다.

profile
LG전자 Connected Service 1 Unit 연구원 변현섭입니다.

0개의 댓글