앱 링크 URL

https://play.google.com/store/apps/details?id=com.poisonrose.memoproject



  




심플한 지도 정보와 간략한 메모를 저장을 할 수 있습니다.

최소한의 터치 조작으로 UI/UX 편의성을 극대화 하였습니다.


1. 기존의 타 메모장들의 불편함을 개선하여 만들었습니다.


2. 메모를 지도 정보와 같이 저장 할 수 있습니다. 


3. 메모 저장시 bg를 선택 할수 있습니다. 리스트 및 맵뷰 내에서 구분되어 보입니다.


4. 내용 클릭시 확인 및 수정이 가능합니다.


5. 상단 돋보기를 누르면 현재 저장된 메모중에 키워드 검색이 가능해집니다.


6. 좌측 메뉴 메모맵 보기를 확인 하시면 내가 저장한 데이터를 지도에서 확인 할 수 있습니다.


기타 문의 사항은 앱 내에서 문의 하기 또는

이메일 문의로 연락 주세요.

안드로이드 구글맵 v2 클러스터 예제를 만들어 보았습니다.

zoomlevel을 내렸을 경우에는 해당 인근의 데이터는 모아서 숫자로 보여주며

올렸을 경우에는 원래 데이터가 있던 위치에 표시되는 지도를 확인 할수 있습니다.


실행 화면

  (Zoom level down)


Java 코드 작업

1. 클러스터 라이브러리를 사용하기 위한 작업을 Gradle에서 추가합니다.

dependencies {
    /* 중략 */
    compile 'com.google.android.gms:play-services-maps:10.2.1'
    compile 'com.google.android.gms:play-services-location:10.2.1'
    compile 'com.google.maps.android:android-maps-utils:0.5'
}


2. ClusterItem 인터페이스 클래스를 작성해줍니다.

import com.google.android.gms.maps.model.LatLng;
import com.google.maps.android.clustering.ClusterItem;

public class PositionItem implements ClusterItem {

    private final LatLng mPosition;
    public int bg;

    public PositionItem(double lat, double lng, int bg) {
        mPosition = new LatLng(lat, lng);
        this.bg = bg;
    }

    @Override
    public LatLng getPosition() {
        return mPosition;
    }

    @Override
    public String getTitle() {
        return null;
    }

    @Override
    public String getSnippet() {
        return null;
    }
}<


3. 맵 호출 부분 작성.

mMap.getMapAsync(new OnMapReadyCallback() {
	@Override
	public void onMapReady(final GoogleMap googleMap) {
		if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
			boolean permission = mParentsAct.hasAllPermissionsGranted();
			if (!permission) {
				return;
			}
		}
		try {
			MapsInitializer.initialize(getActivity());
		} catch (Exception e) {
		}

		googleMap.getUiSettings().setMyLocationButtonEnabled(true);
		googleMap.getUiSettings().setZoomControlsEnabled(true);
		googleMap.setMyLocationEnabled(true);

		setCluster(googleMap);
	}
});


4. setCluster() 메소드 작성.

private void setCluster(final GoogleMap googleMap) {
	final List<PositionItem> mPosi = /* 좌표정보 데이터 리스트 */;
	if(mPosi != null) {
		if (mPosi.size() > 0) {
			mClusterManager = new ClusterManager<PositionItem>(getActivity(), googleMap);
			googleMap.setOnCameraIdleListener(mClusterManager);
			googleMap.setOnMarkerClickListener(mClusterManager);
			
			LatLngBounds.Builder builder = LatLngBounds.builder();	// Bounds 모든 데이터를 맵 안으로 보여주게 하기 위한
			LatLngBounds bounds = addItems(builder, mPosi);         // 클러스터 Marker 추가
			googleMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, ZoomLevel));
			float zoom = googleMap.getCameraPosition().zoom - 0.5f;
			googleMap.animateCamera(CameraUpdateFactory.zoomTo(zoom));

			mClusterManager.setRenderer(new OwnIconRendered(mAppData, googleMap, mClusterManager));

			// 내용 클릭시
			googleMap.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() {
				@Override
				public void onInfoWindowClick(Marker marker) {
				}
			});

			// 클러스터 클릭시 펼치기
			mClusterManager.setOnClusterClickListener(new ClusterManager.OnClusterClickListener<PositionItem>() {
				@Override
				public boolean onClusterClick(Cluster<PositionItem> cluster) {
					LatLngBounds.Builder builder_c = LatLngBounds.builder();
					for (ClusterItem item : cluster.getItems()) {
						builder_c.include(item.getPosition());
					}
					LatLngBounds bounds_c = builder_c.build();
					googleMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds_c, ZoomLevel));
					float zoom = googleMap.getCameraPosition().zoom - 0.5f;
					googleMap.animateCamera(CameraUpdateFactory.zoomTo(zoom));
					return true;
				}
			});
		}
	}
}

// 마커 추가
private void addItems(List<PositionItem> mPosi) {
	for (PositionItem item : mPosi) {
		mClusterManager.addItem(item);
	}
}

// 마커 커스텀 class
class CustomIconRenderer extends DefaultClusterRenderer<PositionItem> {
	public CustomIconRenderer(Context context, GoogleMap map, ClusterManager<PositionItem> clusterManager) {
		super(context, map, clusterManager);
	}

	@Override
	protected void onBeforeClusterItemRendered(PositionItem item, MarkerOptions markerOptions) {
		int id = R.drawable.rect_memo1;
		if(item.bg == 1) {					// drawable 변경
		} else if (item.bg == 2) {
		} else if (item.bg == 3) {
		} else if (item.bg == 4) {
		}
		Drawable d = ContextCompat.getDrawable(getActivity(), id);
		BitmapDescriptor markerIcon = getMarkerIconFromDrawable(d);

		markerOptions.icon(markerIcon);
		markerOptions.snippet(item.getSnippet());
		markerOptions.title(item.getTitle());
		super.onBeforeClusterItemRendered(item, markerOptions);
	}
}


날씨 : 바람 조금, 비오기 전 

체중 : 70.3kg

후기 : 비오기 직전이라 시원하게 달림, 다소 비교적 땀도 적게 나옴

목말라서 물 많이 마심


  


'달리기 흔적' 카테고리의 다른 글

[5km] 2017년 4월 25일 화요일  (0) 2017.04.25
[5km] 2017년 4월 21일  (0) 2017.04.21
[10km] 2017년 4월 16일  (0) 2017.04.16
[5km] 2017년 4월 13일  (0) 2017.04.13
[5km] 2017년 4월 11일  (0) 2017.04.11

Bundle 로 Object, ArrayList 넘기기 예제 입니다.


Intent를 사용하실 경우에는 아래와 같이 사용하며

// 줄때
Intent intent = new Intent();
Bundle bundle = new Bundle();
bundle.putSerializable("Obj", item);   // Object 넘기기
bundle.putParcelableArrayList("list", (ArrayList<? extends Parcelable>) list); // list 넘기기
intent.putExtras(bundle);

// 받을때
Bundle bundle = getIntent().getExtras();
bundle.getSerializable("Obj");
bundle.getParcelableArrayList("list");

Fragment를 통한 사용은 아래와 같이 사용하시면 됩니다.

// 줄때
Fragment f = new Fragment();
Bundle bundle = new Bundle();
bundle.putSerializable("Obj", item);   // Object 넘기기
bundle.putParcelableArrayList("list", (ArrayList<? extends Parcelable>) list); // list 넘기기
f.setArguments(bundle);

// 받을때
Bundle bundle = getArguments();
bundle.getSerializable("Obj");
bundle.getParcelableArrayList("list");


Java 사용 예제

(Serializable)Item 클래스의 코드입니다.

import java.io.Serializable;

public class Item implements Serializable {

    public int index;
    public String address; // 주소
    public String title;   // 제목

    public Item(int index, String address, String title) {
        this.index = index;
        this.address = address;
        this.title = title;
    }
}

(Parcelable)Item2 클래스의 코드입니다.

import android.os.Parcel;
import android.os.Parcelable;

public class Item2 implements Parcelable {

    public int index;
    public String address; // 주소
    public String title;   // 제목

    public Item2(int index, String address, String title) {
        this.index = index;
        this.address = address;
        this.title = title;
    }

    public Item2(Parcel in) {
        index = in.readInt();
        address = in.readString();
        title = in.readString();
    }

    public static final Creator<Item2> CREATOR = new Creator<Item2>() {
        @Override
        public Item2 createFromParcel(Parcel in) {
            return new Item2(in);
        }

        @Override
        public Item2[] newArray(int size) {
            return new Item2[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(index);
        dest.writeString(address);
        dest.writeString(title);
    }
}

보내는 부분의 Java 코드입니다.

Fragment f = new Fragment();
Bundle bundle = new Bundle();
bundle.putInt("id", 1122);

Item item = new Item(123, "abc", "가나다");
// Object 넘기기
bundle.putSerializable("item", item);   

List<Item2> list = new ArrayList<>();
list.add(new Item2(222, "ddd", "다다다"));
list.add(new Item2(223, "eee", "다다다"));
list.add(new Item2(224, "ddd", "다다다"));
// 리스트 넘기기
bundle.putParcelableArrayList("list", (ArrayList<? extends Parcelable>) list);

f.setArguments(bundle);

받는 부분의 Java 코드입니다.

Bundle bundle = getArguments();
int id = bundle.getInt("id");
Item item = (Item) bundle.getSerializable("item");
List<Item2> item2 = bundle.getParcelableArrayList("list");

Log.v("test","id : "+id);
Log.w("test","item : "+item.index+" , "+item.address+" , "+item.title);
Log.d("test","item2 : "+item2.size());
for(Item2 it : item2)
	Log.i("test","it : "+it.index+" , "+it.address+" , "+it.title);

결과 화면





sqlite 에서 Like 검색 예제입니다.

실행 화면은 아래와 같습니다.


 


코드 작성

UI 처리 부분

public void setChangeListData(String searchKeyword) {
	if(searchKeyword != null) {
		if (searchKeyword.length() != 0) {
			List<ItemMemo> temp = setLikeSearch(searchKeyword);
			if(temp != null) {
				setSearchData(temp);
				return;
			}
		}
	}
	setLoadListData(oriList);
}

private void setSearchData(List<ItemMemo> list) {
	lay_noData.setVisibility(View.GONE);
	tv_noSearch.setVisibility(View.GONE);
	if(list.size() == 0) {
		rv.setVisibility(View.INVISIBLE);
		tv_noSearch.setVisibility(View.VISIBLE);
	} else {
		rv.setVisibility(View.VISIBLE);
		mAdapter.swap(list);
	}
}


SQLite 쿼리 날리는 부분

/**
 * Like 검색
 */
public List<ItemMemo> setLikeSearch(String keyword) {
	List<ItemMemo> list = new ArrayList<>();
	try {
		beginTransaction();
		Cursor c = select(TABLE_NAME, /**COLUMNS_NAME*/"contents", keyword);
		if (c != null) {
			Log.i("test","c : " + c + " , " + c.getCount());
			int total = c.getCount();
			if (total > 0) {
				c.moveToFirst();
				while (!c.isAfterLast()) {
					
					/* 중략 */

					list.add(new ItemMemo());
					c.moveToNext();
				}
			}
			c.close();
		}
	} catch (SQLiteException e) {
		Log.e("test", e.toString());
	} finally {
		endTransaction();
	}
	return list;
}

public Cursor select(String tableName, String name, String Like) throws SQLiteException{
	return getReadableDatabase().query(
			tableName, 
			null,
			name+ " LIKE ?",
			new String[] {"%"+ Like+ "%" },
			null,
			null,
			null,
			null);
}


Listview 내에서 arraylist 의 초성 검색 예제입니다.

참고 url

http://www.androidpub.com/45681


   


HangulUtils.cass 파일을 생성합니다.

/**
 * com.ziofront.utils
 * HangulUtils.java
 * Jiho Park 2009. 11. 27.
 *
 * Copyright (c) 2009 ziofront.com. All Rights Reserved.
 */
public class HangulUtils {

	private static String toHexString(int decimal) {
		Long intDec = Long.valueOf(decimal);
		return Long.toHexString(intDec);
	}

	public static final int HANGUL_BEGIN_UNICODE = 44032; // 가
	public static final int HANGUL_END_UNICODE = 55203; // 힣
	public static final int HANGUL_BASE_UNIT = 588;

	public static final int[] INITIAL_SOUND_UNICODE = { 12593, 12594, 12596,
			12599, 12600, 12601, 12609, 12610, 12611, 12613, 12614, 12615,
			12616, 12617, 12618, 12619, 12620, 12621, 12622 };

	public static final char[] INITIAL_SOUND = { 'ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ',
			'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ' };

	/**
	 * 문자를 유니코드(10진수)로 변환 후 반환 한다.
	 * 
	 * @param ch
	 * @return
	 */
	public static int convertCharToUnicode(char ch) {
		return (int) ch;
	}

	/**
	 * 문자열을 유니코드(10진수)로 변환 후 반환 한다.
	 * 
	 * @param str
	 * @return
	 */
	public static int[] convertStringToUnicode(String str) {
		int[] unicodeList = null;
		if (str != null) {
			unicodeList = new int[str.length()];
			for (int i = 0; i < str.length(); i++) {
				unicodeList[i] = convertCharToUnicode(str.charAt(i));
			}
		}

		return unicodeList;
	}

	/**
	 * 유니코드(16진수)를 문자로 변환 후 반환 한다.
	 * 
	 * @param hexUnicode
	 * @return
	 */
	public static char convertUnicodeToChar(String hexUnicode) {
		return (char) Integer.parseInt(hexUnicode, 16);
	}

	/**
	 * 유니코드(10진수)를 문자로 변환 후 반환 한다.
	 * 
	 * @param unicode
	 * @return
	 */
	public static char convertUnicodeToChar(int unicode) {
		return convertUnicodeToChar(toHexString(unicode));
	}

	/**
	 * 
	 * @param value
	 * @return
	 */
	public static String getHangulInitialSound(String value) {
		StringBuffer result = new StringBuffer();
		int[] unicodeList = convertStringToUnicode(value);
		for (int unicode : unicodeList) {

			if (HANGUL_BEGIN_UNICODE <= unicode
					&& unicode <= HANGUL_END_UNICODE) {
				int tmp = (unicode - HANGUL_BEGIN_UNICODE);
				int index = tmp / HANGUL_BASE_UNIT;
				result.append(INITIAL_SOUND[index]);
			} else {
				result.append(convertUnicodeToChar(unicode));

			}
		}
		return result.toString();
	}

	public static boolean[] getIsChoSungList(String name) {
		if (name == null) {
			return null;
		}
		boolean[] choList = new boolean[name.length()];
		for (int i = 0; i < name.length(); i++) {
			char c = name.charAt(i);

			boolean isCho = false;
			for (char cho : INITIAL_SOUND) {
				if (c == cho) {
					isCho = true;
					break;
				}
			}
			choList[i] = isCho;
		}
		return choList;
	}

	public static String getHangulInitialSound(String value,
			String searchKeyword) {
		return getHangulInitialSound(value, getIsChoSungList(searchKeyword));
	}

	public static String getHangulInitialSound(String value, boolean[] isChoList) {
		StringBuffer result = new StringBuffer();
		int[] unicodeList = convertStringToUnicode(value);
		for (int idx = 0; idx < unicodeList.length; idx++) {
			int unicode = unicodeList[idx];

			if (isChoList != null && idx <= (isChoList.length - 1)) {
				if (isChoList[idx]) {
					if (HANGUL_BEGIN_UNICODE <= unicode
							&& unicode <= HANGUL_END_UNICODE) {
						int tmp = (unicode - HANGUL_BEGIN_UNICODE);
						int index = tmp / HANGUL_BASE_UNIT;
						result.append(INITIAL_SOUND[index]);
					} else {
						result.append(convertUnicodeToChar(unicode));
					}
				} else {
					result.append(convertUnicodeToChar(unicode));
				}
			} else {
				result.append(convertUnicodeToChar(unicode));
			}
		}

		return result.toString();
	}

	public static void main(String[] args) {
		for (char ch : HangulUtils.INITIAL_SOUND) {
			System.out.print(HangulUtils.convertCharToUnicode(ch) + ",");
		}
	}
}


ListView뷰를 구성합니다.

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="match_parent"
    android:background="@color/colorBg">

    <android.support.v7.widget.RecyclerView
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:background="@color/colorBg"
        android:id="@+id/rv">
    </android.support.v7.widget.RecyclerView>

    <!-- no data -->
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="20dp"
        android:id="@+id/tv_noSearch"
        android:textColor="#000000"
        android:text="검색된 메모가 없습니다"
        android:visibility="invisible"
        android:textSize="16sp"/>
</RelativeLayout>


EditText 내에서의 TextWathcher 작업을 합니다.

search_view = (EditText) findViewById(R.id.search_view); search_view.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void onTextChanged(CharSequence c, int i, int i1, int i2) { setChangeListData(c.toString()); } @Override public void afterTextChanged(Editable editable) { } });


setChangeListData 메소드를 작성합니다.

public void setChangeListData(String searchKeyword) {
	if(searchKeyword != null) {
		if (searchKeyword.length() == 0) {
			setLoadListData(oriList);
		} else {
			List<ItemMemo> temp = new ArrayList<>();
			for(ItemMemo i : mList) {
				boolean isAdd = false;
				String iniName = HangulUtils.getHangulInitialSound(i.contents, searchKeyword);
				if (iniName.indexOf(searchKeyword) >= 0) {
					isAdd = true;
				}
				if(isAdd) {
					temp.add(i);
				}
			}
			setSearchData(temp);
		}
	} else {
		setLoadListData(oriList);
	}
}

private void setSearchData(List<ItemMemo> list) {
	lay_noData.setVisibility(View.GONE);
	tv_noSearch.setVisibility(View.GONE);
	if(list.size() == 0) {
		rv.setVisibility(View.INVISIBLE);
		tv_noSearch.setVisibility(View.VISIBLE);
	} else {
		rv.setVisibility(View.VISIBLE);
		mAdapter.swap(list);
	}
}


SNS에서 게시물 같은 곳에 특정시간을 불러와서 현재시간이랑 비교 후 분,시,일,월,년 계산 해서

스트링 반환하는 예제입니다.


 코드 사용

regTime은 계산할 시간이고,

curTime은 현재 시간입니다.

/** 몇분전, 방금 전, */
private static class TIME_MAXIMUM{
	public static final int SEC = 60;
	public static final int MIN = 60;
	public static final int HOUR = 24;
	public static final int DAY = 30;
	public static final int MONTH = 12;
}
public static String formatTimeString(long regTime) {
	long curTime = System.currentTimeMillis();
	long diffTime = (curTime - regTime) / 1000;
	String msg = null;
	if (diffTime < TIME_MAXIMUM.SEC) {
		msg = "방금 전";
	} else if ((diffTime /= TIME_MAXIMUM.SEC) < TIME_MAXIMUM.MIN) {
		msg = diffTime + "분 전";
	} else if ((diffTime /= TIME_MAXIMUM.MIN) < TIME_MAXIMUM.HOUR) {
		msg = (diffTime) + "시간 전";
	} else if ((diffTime /= TIME_MAXIMUM.HOUR) < TIME_MAXIMUM.DAY) {
		msg = (diffTime) + "일 전";
	} else if ((diffTime /= TIME_MAXIMUM.DAY) < TIME_MAXIMUM.MONTH) {
		msg = (diffTime) + "달 전";
	} else {
		msg = (diffTime) + "년 전";
	}
	return msg;
}


적용 후


현재 fragment 가져오기
및 instanceof 로 fragment 구분하기

코드 사용

// 현재 프레그멘트 가져오기
Fragment fragment = getFragmentManager().findFragmentById(R.id.frame);
if(fragment instanceof MyFragment) {
	// todo 	
}


android cardview background color 적용하기


java 코드

setCardBackgroundColor 메소드를 사용하시면 됩니다.

int color = R.color.color_black;

CardView lay_frame = (CardView) itemView.findViewById(R.id.lay_frame);
lay_frame.setCardBackgroundColor(ContextCompat.getColor(getActivity(), res));


or xml 코드

cardBackgroundColor 애튜리뷰트를 사용하면 됩니다.

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView 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:foreground="?android:attr/selectableItemBackground"
    android:id="@+id/lay_frame"
    android:layout_marginTop="8dp"
    android:layout_marginLeft="10dp"
    android:layout_marginRight="10dp"
    card_view:cardCornerRadius="5dp"
    card_view:cardBackgroundColor="@color/color_black">



날씨 : 선선함

미세먼지 : 적음

체중 : 71.0kg



후기 : 뛰고 나니 더움, 다음부턴 반바지 입어도 될듯, 6km 이후 급 힘들어짐

목이 많이 마름, 땀 많이 남



  




'달리기 흔적' 카테고리의 다른 글

[5km] 2017년 4월 25일 화요일  (0) 2017.04.25
[5km] 2017년 4월 21일  (0) 2017.04.21
[5km] 2017년 4월 18일  (0) 2017.04.18
[5km] 2017년 4월 13일  (0) 2017.04.13
[5km] 2017년 4월 11일  (0) 2017.04.11

+ Recent posts