티스토리 뷰
웹이든 앱이든 개발자들이 한 번씩은 경험해 보는 것이 쇼핑몰일 것이다. 그만큼 기본적으로 할 수 있어야 하는 부분들을 할 수 있고, 개발실력 향상에 도움이 되어서가 아닌가 생각된다.
안드로이드 개발 실력을 스스로 확인하기위해, 약 한 달의 시간에 걸쳐 천천히 쇼핑몰 개발을 진행하였다.
아래는 개발하기 전에 세웠던 목표들이다.
- 로딩화면에서 애니메이션을 넣어 로그인 화면 전환을 부드럽게 만들어보자.
- RecyclerView를 사용하자.
- BottomNavigationBar 를 사용하자.
- SQLite를 써서 개발해보자.
- 자동로그인 기능도 구현해보자.
결론적으로 위 목표를 모두 실현했다.
코드
- Login Activity -
우선, 프로젝트를 막 시작하며 Animation을 구현하면서 써놓았던 글이 있는데, 거기에 로고와 약간의 수정만 한 후 커스텀 로그인 폼을 추가해서 로딩 화면을 완성했다.
private PreferenceManager pManager;
...
private void startAnime() {
logoAni = new TranslateAnimation(0, 0, 0, -250); // from 어디서 to 어디까지 이동할건지. 가운데를 중심으로 위, 왼쪽: - 아래, 오른쪽: +
logoAni.setDuration(2000); // 지속시간
logoAni.setFillAfter(true); // 이동 후 이동한 자리에 남아있을건지
logoAni.setStartOffset(1500); // 딜레이
logoAni.setInterpolator(new AccelerateDecelerateInterpolator()); // interpolator 설정. AccelerteDecelerate : 시작지점에 가속했다 종료시점에 감속
loginFormAni = new AlphaAnimation(0, 1);
loginFormAni.setDuration(1000);
loginFormAni.setStartOffset(3000);
imgView.setAnimation(logoAni); // 애니메이션 세팅
loginForm.setAnimation(loginFormAni);
}
public void loginCheck() {
String loginId = pManager.getString(this, "user_id");
if(loginId.length() != 0) { // preference가 비어있지 않으면 바로 Main실행.
startMainActivity();
}
}
private boolean accountCheck() {
//db에 접근해서 id, pw 확인 후 일치 시 true, 불일치시 false return.
String idValue = String.valueOf(id.getText());
String pwValue = String.valueOf(pw.getText());
if(idValue.equals("") || pwValue.equals("")) {
return false;
}
String dbId = dbHelper.getUserId(idValue);
String dbPw = dbHelper.getUserPw(idValue);
if(dbId.equals(idValue)) {
if(dbPw.equals(pwValue)) {
Toast.makeText(this, "환영합니다.", Toast.LENGTH_SHORT).show();
return true;
}
Toast.makeText(this, "비밀번호를 확인해주세요.", Toast.LENGTH_SHORT).show();
return false;
}
setEmptyEt();
Toast.makeText(this, "존재하지 않는 아이디입니다.", Toast.LENGTH_SHORT).show();
return false;
}
private void setEmptyEt() {
id.setText("");
pw.setText("");
}
...
- Register Activity -
public void onOkBtnClick(View v) {
if(isEmpty()) { // 비어있는 칸 없는지 확인. 있으면 true 반환
return;
} else if(isDuplicateId()) { // ID값 중복된 것 있는지 확인. 있으면 true 반환
Toast.makeText(this, "이미 있는 ID 입니다. ID를 변경해주세요.", Toast.LENGTH_SHORT).show();
return;
} else if(!isCorreectPw()) { // PW, PwChk 값 같은지 확인. 같으면 true 반환
Toast.makeText(this, "비밀번호와 비밀번호 확인의 값이 다릅니다. 다시 확인해주세요.", Toast.LENGTH_SHORT).show();
return;
}
// Email 포맷 체크, %@% 아니면 DB에 안들어감.
// DB에 값 집어넣기
UserBean userBean = new UserBean();
userBean.setName(String.valueOf(name.getText()));
userBean.setEmail(String.valueOf(email.getText()));
userBean.setId(String.valueOf(id.getText()));
userBean.setPassword(String.valueOf(pw.getText()));
userBean.setGender(String.valueOf(selectGender));
userBean.setYears(String.valueOf(selectYears));
dbHelper.insertUser(userBean);
Toast.makeText(this, "회원가입이 완료되었습니다.", Toast.LENGTH_SHORT).show();
finish();
}
- Main Activity -
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 첫 화면 지정
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.frameLayout, homeFragment).commitAllowingStateLoss();
BottomNavigationView bottomNav = findViewById(R.id.bottom_navigation_view);
bottomNav.setOnNavigationItemSelectedListener(new ItemSelectListener());
}
private class ItemSelectListener implements BottomNavigationView.OnNavigationItemSelectedListener{
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
FragmentTransaction transaction = fragmentManager.beginTransaction();
switch (menuItem.getItemId()) {
case R.id.nav_home:
transaction.replace(R.id.frameLayout, homeFragment).commitAllowingStateLoss();
break;
case R.id.nav_shop:
transaction.replace(R.id.frameLayout, shopFragment).commitAllowingStateLoss();
break;
case R.id.nav_cart:
transaction.replace(R.id.frameLayout, cartFragment).commitAllowingStateLoss();
break;
case R.id.nav_my:
transaction.replace(R.id.frameLayout, myFragment).commitAllowingStateLoss();
break;
}
return true;
}
}
@Override
public void onBackPressed() {
ActivityCompat.finishAffinity(this); // app 종료
}
마지막 onBackPressed()의 ActivityCompat.finishAffinity(this) 는, RootActivity까지 나가 finish()를 하지 말고 바로 종료시키고 싶었기 때문에 넣었다.
- Home Fragment -
private static final int INTERVAL_TIME = 3800;
private View rootView;
private ViewFlipper viewFlipper;
private GridView gridView;
private HomeGridAdapter adapter;
private ArrayList<ProductBean> data;
private ProductDBHelper dbHelper;
int images[] = {
R.drawable.slide_image_1,
R.drawable.slide_image_2,
R.drawable.slide_image_3,
R.drawable.slide_image_4
};
// 메인. 슬라이드 형식 화면 절반치 광고, 아래에 상품 6개 정도 보여주기
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.activity_home_fragment, container, false);
viewFlipper = view.findViewById(R.id.imageSlide);
for(int image : images)
flipperImages(image);
showProduct();
return view;
}
private void flipperImages(int image) {
ImageView imageView = new ImageView(getContext());
imageView.setBackgroundResource(image);
viewFlipper.addView(imageView);
viewFlipper.setFlipInterval(INTERVAL_TIME);
viewFlipper.setAutoStart(true);
viewFlipper.setInAnimation(getContext(), R.anim.slide_in_anim);
viewFlipper.setOutAnimation(getContext(), R.anim.slide_out_anim);
}
private void showProduct() {
dbHelper = ProductDBHelper.getInstance(getContext());
data = dbHelper.getRandomProduct();
gridView = view.findViewById(R.id.gridView);
adapter = new HomeGridAdapter(getContext(), data);
gridView.setAdapter(adapter);
}
Fragment를 이렇게 많이쓴 프로젝트는 처음이었다. Activity와 달리 onCreateView를 사용해야 하며, findViewById를 할 때에는 따로 view를 inflate 한 후 사용해야 한다는 점이 어색했었다.
- UserDBHelper -
public class UserDBHelper extends SQLiteOpenHelper {
private static UserDBHelper dbHelper = null;
public static final String DATABASE_NAME = "userdb";
public static final String TABLE_NAME = "user";
public static final int DB_VERSION = 1;
public static final String COL_0 = "serialNumber";
public static final String COL_1 = "name";
public static final String COL_2 = "email";
public static final String COL_3 = "id";
public static final String COL_4 = "password";
public static final String COL_5 = "gender";
public static final String COL_6 = "years";
public static UserDBHelper getInstance(Context context){
if(dbHelper == null){
dbHelper = new UserDBHelper(context.getApplicationContext());
}
return dbHelper;
}
private UserDBHelper(Context context){
super(context, DATABASE_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
String sql = "create table " + TABLE_NAME + " ("
+ COL_0 + " integer primary key autoincrement, "
+ COL_1 + " text not null,"
+ COL_2 + " text not null check (email like '%@%'),"
+ COL_3 + " text not null unique,"
+ COL_4 + " text not null,"
+ COL_5 + " text not null,"
+ COL_6 + " text not null "
+ ")";
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
String sql = "drop table " + TABLE_NAME;
db.execSQL(sql);
onCreate(db);
}
public long insertUser(UserBean user){
SQLiteDatabase db = getWritableDatabase();
ContentValues value = new ContentValues();
value.put(COL_1, user.getName());
value.put(COL_2, user.getEmail());
value.put(COL_3, user.getId());
value.put(COL_4, user.getPassword());
value.put(COL_5, user.getGender());
value.put(COL_6, user.getYears());
return db.insert(TABLE_NAME, null, value);
}
public ArrayList<UserBean> getAllUser(){
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, null, null, null, null, null, null);
ArrayList<UserBean> result = new ArrayList<>();
while(cursor.moveToNext()){
UserBean user = new UserBean();
user.setSerialNumber(cursor.getInt(cursor.getColumnIndex(COL_0)));
user.setName(cursor.getString(cursor.getColumnIndex(COL_1)));
user.setEmail(cursor.getString(cursor.getColumnIndex(COL_2)));
user.setId(cursor.getString(cursor.getColumnIndex(COL_3)));
user.setPassword(cursor.getString(cursor.getColumnIndex(COL_4)));
user.setGender(cursor.getString(cursor.getColumnIndex(COL_5)));
user.setYears(cursor.getString(cursor.getColumnIndex(COL_6)));
result.add(user);
}
return result;
}
public ArrayList<UserBean> getUserById(String id){
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, null, COL_3 + "=?", new String[] {id}, null, null, null);
ArrayList<UserBean> result = new ArrayList<>();
while(cursor.moveToNext()){
UserBean user = new UserBean();
user.setSerialNumber(cursor.getInt(cursor.getColumnIndex(COL_0)));
user.setName(cursor.getString(cursor.getColumnIndex(COL_1)));
user.setEmail(cursor.getString(cursor.getColumnIndex(COL_2)));
user.setId(cursor.getString(cursor.getColumnIndex(COL_3)));
user.setPassword(cursor.getString(cursor.getColumnIndex(COL_4)));
user.setGender(cursor.getString(cursor.getColumnIndex(COL_5)));
user.setYears(cursor.getString(cursor.getColumnIndex(COL_6)));
result.add(user);
}
return result;
}
public String getUserId(String id){
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, null, COL_3 + "=?", new String[] {id}, null, null, null);
String result = "";
while(cursor.moveToNext()){
result = cursor.getString(cursor.getColumnIndex(COL_3));
}
return result;
}
public String getUserPw(String id){
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, null, COL_3 + "=?", new String[] {id}, null, null, null);
String result = "";
while(cursor.moveToNext()){
result = cursor.getString(cursor.getColumnIndex(COL_4));
}
return result;
}
public long deleteUser(UserBean bean){
SQLiteDatabase db = getWritableDatabase();
String serial = String.valueOf(bean.getSerialNumber());
return db.delete(TABLE_NAME, COL_0 + "=?", new String[] {serial});
}
}
SQLite를 이용하면서, rowQurey와 SQLiteDatabase에서 제공하는 query를 섞어서 사용했다. 그냥 이것저것 다양한 방법을 사용해보고 싶었다.
후기
사실 프로젝트를 진행하면서 코드의 퀄리티보다는 완성에만 집중했었던 것 같다. 때문에 RecyclerView가 필요없는 부분에서도 써놓았던 코드를 Ctrl + C, V 하여 작성하는 참사가 발생했었다. 지금에야 GridView를 사용하는 방법으로 리사이클링을 했지만, 지금 보면 왜 그랬었을까 싶다. 그래도 개발에 대해서 많은 생각을 키울 수 있었던 프로젝트였다.
개발기간 : 2019.09.24. ~ 2019.10.22.
https://github.com/gurdlwl/Android_ShoppingMall
gurdlwl/Android_ShoppingMall
android 쇼핑몰. Contribute to gurdlwl/Android_ShoppingMall development by creating an account on GitHub.
github.com
'프로젝트 > 개인' 카테고리의 다른 글
[Python] Face Finder (0) | 2019.06.26 |
---|---|
[Android] 내 손안에 장생포 재개발 (2) | 2019.06.21 |
[C#] 한솥 매장 계산대 Project (0) | 2019.04.24 |
[Web] Apple Homepage Benchmarking Project (0) | 2019.04.23 |