728x90

안드로이드 어플에서 이미지가 수평으로 일정시간후 자동으로 화면이 회전되는 CarouselView 를 검색하여 https://github.com/sayyam/carouselview 이 있는 걸 다운로드 해서 테스트해보고 적어둔다.


위 예제는 Web에 올려진 이미지를 Picasso 이미지 라이브러리를 이용하여 보여주는 것과 Drawable 폴더에 있는 이미지를 보여주는 것을 하나의 화면에서 보여주고 있다.


이미지 라이브러리에 대한 자세한 설명은 http://gun0912.tistory.com/17  와 http://gun0912.tistory.com/19 를 참조하면 도움이 많이 된다.


Web 에서 가져온 이미지의 경우 잔상이 좀 남아서 약간 눈에 거슬려보이기는 하지만 기능은 잘 동작된다.

CarouselView 라이브러리가 기능적으로 편리하게 사용하도록 되어 있는거 같다.


아래 내용은 이해하기 쉽게 용어를 변경하고 기능을 테스트했다.

이미지 롤링 보여주기 용도로는 편하고 좋은거 같은데 달력을 carousel 하기에는 적합하지 않은 거 같아서 다른 걸 추가로 테스트 해보려고 한다.


앱 build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "25.0.0"

    defaultConfig {
        applicationId "com.tistory.link2me.carouselview"
        minSdkVersion 19
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:23.2.1'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    compile 'com.synnapps:carouselview:0.0.9'
    compile 'com.squareup.picasso:picasso:2.5.2'  // 이미지 라이브러리
    compile 'com.android.support:recyclerview-v7:23.2.1' // ListView 개선 버전
    compile "com.android.support:cardview-v7:23.2.1"

}


AndroidManifest.xml 에 추가할 사항

<uses-permission android:name="android.permission.INTERNET" />


Layout xml


 <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center">

    <ImageView
        android:id="@+id/fruitImageView"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="centerCrop" />

    <TextView
        android:id="@+id/labelTextView"
        style="@style/TextView.Label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>



MainActivity.java

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Gravity;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

import com.squareup.picasso.Picasso;
import com.synnapps.carouselview.CarouselView;
import com.synnapps.carouselview.ImageListener;
import com.synnapps.carouselview.ViewListener;

public class MainActivity extends AppCompatActivity {
    CarouselView localCarouselView;
    TextView localCarouselLabel;
    String[] TitleNames = {"Orange", "Grapes", "Strawberry", "Cherry", "Apricot"};
    int[] localImages = {R.drawable.image_1,
                          R.drawable.image_2,
                          R.drawable.image_3,
                          R.drawable.image_4,
                          R.drawable.image_5
    };

    CarouselView webCarouselView;
    TextView webCarouselLabel;
    String[] webImageURLs = {
            Value.IPADDRESS + "/img_01.jpg",
            Value.IPADDRESS + "/img_02.jpg",
            Value.IPADDRESS + "/img_03.jpg",
            Value.IPADDRESS + "/img_04.jpg",
            Value.IPADDRESS + "/img_05.jpg"
    };

    Button pauseButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
    }

    private void initView(){

        webCarouselLabel = (TextView) findViewById(R.id.webCarouselLabel);
        // Web 서버 URL 이미지 롤링배너로 보여주기
        webCarouselView = (CarouselView) findViewById(R.id.webCarouselView);
        webCarouselView.setPageCount(webImageURLs.length);
        webCarouselView.setImageListener(new ImageListener() {
            @Override
            public void setImageForPosition(int position, ImageView imageView) {
                Picasso.with(getApplicationContext())
                        .load(webImageURLs[position])
                        .placeholder(localImages[0])
                        .error(localImages[3])
                        .fit().centerCrop()
                        .into(imageView);
            }
        });
        webCarouselView.setIndicatorGravity(Gravity.CENTER_HORIZONTAL|Gravity.BOTTOM);

        localCarouselLabel = (TextView) findViewById(R.id.localCarouselLabel);
        // drawable 이미지 롤링배너 보여주기
        localCarouselView = (CarouselView) findViewById(R.id.localCarouselView);
        localCarouselView.setPageCount(localImages.length);
        localCarouselView.setSlideInterval(4000);
        localCarouselView.setViewListener(new ViewListener() {
            @Override
            public View setViewForPosition(int position) {
                View customView = getLayoutInflater().inflate(R.layout.view_custom, null);

                TextView labelTextView = (TextView) customView.findViewById(R.id.labelTextView);
                ImageView fruitImageView = (ImageView) customView.findViewById(R.id.fruitImageView);

                fruitImageView.setImageResource(localImages[position]);
                labelTextView.setText(TitleNames[position]);

                localCarouselView.setIndicatorGravity(Gravity.CENTER_HORIZONTAL|Gravity.TOP);

                return customView;
            }
        });

        pauseButton = (Button) findViewById(R.id.pauseButton);
        pauseButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                webCarouselView.pauseCarousel();
                localCarouselView.reSetSlideInterval(0);
            }
        });
    }
}


위 테스트에 사용한 소스코드중에서 필요한 것만 발췌했다.


carouselview_ex1.zip



블로그 이미지

Link2Me

,
728x90

기사 전문은 http://news.hankyung.com/article/2017103082191에 있고 일부 기사만 발췌하여 적었다.


구리~포천 간 민자고속도로 개통 4개월을 맞은 30일 경기 포천시가 산업단지 분양률이 높아지고 관광객이 증가하는 등 경기 북부권의 기업 및 관광 중심지로 떠오르고 있다.

김종천 포천시장(사진)은 30일 한국경제신문과의 인터뷰에서 “포천시가 민자도로 개통 이후 기업인과 관광객이 찾는 활기찬 도시로 변화하고 있다”며 “도내 최하위권인 시세를 5년 내 10위권 수준으로 높이겠다”고 강조했다.
 
민자고속도로 포천나들목에 인접해 있는 용정산단은 민자도로 개통 전 40%에 불과했던 분양률이 77%로 높아졌다. 서울 잠실에서 30분 내 도달할 수 있는 교통 여건과 부지가격이 ㎡당 119만원으로 의정부 등 인근 지역 산단에 비해 80만~100만원가량 저렴하기 때문이다. 김 시장은 “허브아일랜드를 찾는 관광객이 월평균 6만 명에서 10% 정도 늘었다”며 “민자도로 개통이 분양률 상승과 관광 활성화에 기여하고 있다”고 소개했다.


포천시는 민자도로 개통을 계기로 기업도시 육성과 한탄강 일대 관광지 개발 등 지역발전 전략을 마련했다. 김 시장은 “95만㎡ 부지에 100개 기업을 유치할 용정산단은 77개 기업이 입주를 결정했고, 장자·에코그린산단 등의 분양률은 입주기업이 늘면서 60%대로 올라섰다”고 소개했다. 시는 최근 들어 기업들의 입주문의가 늘자 8000억원을 들여 50만㎡ 규모의 K-디자인밸리산단을 내년 12월 착공 목표로 추진하기로 했다.


민자도로가 개통된 이후 포천을 찾는 관광객이 늘면서 지역경제에 활력이 되고 있다. 김 시장은 “인천공항에서 자동차로 2시간30분 걸리던 것이 지금은 한 시간으로 줄어 한탄강 둘레길(영북면 부소천~비둘기낭 폭포, 6.2㎞)과 허브아일랜드 등에 동남아시아 관광객이 많이 찾고 있다”며 “연평균 780만 명인 관광객이 올해 10% 정도 늘 것”이라고 설명했다.

김 시장은 관광객 유치를 위해 2019년까지 국내 유일의 현무암 협곡이 있는 한탄강 관광지 일대에 548억원을 투자해 개발하기로 했다. 주상절리길, 생태관광단지 등 6개 한탄강 관광테마사업을 추진한다. 영중면 힐마루관광레저 조성사업 등 20여 개 관광지도 개발하기로 했다. 김 시장은 “포천을 경기북부권 관광의 핵심지로 조성하기 위한 것”이라며 “신평~가양 간 도로 확·포장과 자작~어룡 도로 개설 등 6~7개 사업을 추진해 산단과 관광지를 활성화하겠다”고 강조했다.


블로그 이미지

Link2Me

,
728x90

회원가입시 필요한 member Class 함수다.

적용하는 사이트에 따라 칼럼(필드)가 추가로 있기도 하고 없을 수도 있는 걸 고려한 회원 등록, 회원 정보 수정 기능이 포함되어 있다.

패스워드 저장방식은 원하는 걸로 수정하면 된다.


 <?php
class MemberClass {
    //회원 신규 입력
    function MemberRegister($arr){
        date_default_timezone_set('Asia/Seoul');
        $rs=$this->MemberIDCheck($arr['idx'],$arr['userID']); //아이디중복검사
        if($rs<0){ //아이디 중복
            return -1;
            exit;
        } else if($rs==1){ //아이디 입력가능
            $sql="insert into member (idx,userID,passwd,userNM,telNO,mobileNO,email,Cate1,Cate2,smart,";
            if(isset($arr['positionNM']))
                $sql.="positionNM,";
            $sql.="allowLogin,regdate) values(NULL,";
            $sql.="'".$arr['userID']."',";
            $sql.="md5('".substr($arr['mobileNO'],-4)."'),";
            $sql.="'".$arr['userNM']."',";
            $sql.="'".$this->SplitRemove($arr['telNO'])."',";
            $sql.="'".$this->SplitRemove($arr['mobileNO'])."',";
            $sql.="'".$arr['email']."',";
            $sql.="'".$arr['Cate1']."',";
            $sql.="'".$arr['Cate2']."',";
            $sql.="'".$arr['smart']."',";
            if(isset($arr['positionNM'])) // 변수가 있으면 추가하라
                $sql.="'".$arr['positionNM']."',";
            $sql.="'".$arr['allowLogin']."',";
            $sql.="'".date("Ymd")."'"; // 등록일자
            if($result=mysql_query($sql)){
                return mysql_insert_id();
            } else {
                return $sql;
            }

        } else {//알수없는 Db오류
            return $sql;
        }
    }

    //회원정보 수정
    function MemberModify($arr){
        global $db;
        date_default_timezone_set('Asia/Seoul');

        $rs=$this->MemberIDCheck($arr['idx'],$arr['userID']);
        if($rs<0){ //아이디 중복
            return -1;
            exit;
        } else if($rs==1){ //아이디 입력가능
            $sql="update member set ";
            $sql.="userID='".$arr['userID']."',";
            if(isset($arr['passwd'])){
                $sql.="passwd=md5('".$arr['passwd']."'),";
            }
            $sql.="userNM='".$arr['userNM']."',";
            $sql.="telNO='".$this->SplitRemove($arr['telNO'])."',";
            $sql.="mobileNO='".$this->SplitRemove($arr['mobileNO'])."',";
            $sql.="email='".$arr['email']."',";
            $sql.="Cate1='".$arr['Cate1']."',";
            $sql.="Cate2='".$arr['Cate2']."',";
            $sql.="smart='".$arr['smart']."',";
            if(isset($arr['positionNM'])){
                $sql.="positionNM='".$arr['positionNM']."',";  
            }
            if(isset($arr['allowLogin'])){
                $sql.="allowLogin='".$arr['allowLogin']."',";
            }
            if(isset($arr['hidden'])){
                $sql.="hidden='".$arr['hidden']."',";
            }
            $sql.="regdate='".date("Ymd")."'"; // 수정일자
            $sql.=" where idx=".$arr['idx'];
            if($result=mysql_query($sql)){
                return $arr['idx'];
            } else {
                return $sql;
            }

        } else {//알수없는 Db오류
            return 0;
        }

    } //end function


    //회원정보 삭제
    function
MemberDelete($idx){
        global $db;
        $sql="delete from member where idx=".$idx;
        if($result=mysql_query($sql)){
            return 1;
        } else {
            return $sql;
        }
    }

    //변경전 로그인ID 유일성체크
    function MemberIDCheck($idx,$userID){
        global $db;
        $sql="select idx,count(*) from member where userID='".$userID."'";
        if($result=mysql_query($sql)){
            $row=mysql_fetch_row($result);
            if($row[1]==1){ //id존재
                if($row[0]==$idx){ // id변경없음
                    return 1;
                } else { //id변경시도, 다른 id존재, 변경불가
                    return -1;
                }
            } else { //변경시도, 다른id없음, 변경가능
                return 1;
            }
        } else {
            return 0;
        }
    }

}
?>


사용법

<?php
require_once 'memberClass.php';
$m = new MemberClass(); // 객체 생성

// 회원가입시 입력 form 에서 넘겨받은 배열 정보를 넣는다.
$rs = $m->MemberRegister($_POST);

// 회원 정보 수정시 입력 form 에서 넘겨받은 배열 정보를 넣는다.
$rs = $m->MemberModify($_POST);
echo $rs; // Ajax 통신인 경우 결과값을 반환하여 데이터 수정 성공/실패 팝업 메시지를 띄운다.
?>

블로그 이미지

Link2Me

,
728x90

DB 결과가 숫자로 0, 1로 되어 있을 경우 화면 출력과 수정할 수 있게 하는 코드


    echo "<div data-role='fieldcontain'>";
    echo "<label>&nbsp;개인정보&nbsp;: </label>";
        $personInfo=array('비공개','공개동의');
        foreach($personInfo as $k=>$v){
            if($row['smart']==$k){
                echo "<label>".$v."</label>";
            }
        }
    echo "</div>";


   echo "<div data-role='fieldcontain'>";
    echo "<table>";
    echo "<tr>";
    echo "<td><label>개인정보&nbsp;: </label></td>";
    echo "<td><select class='modistaff' name='smart'>";
        $personInfo=array('비공개','공개동의');
        foreach($personInfo as $k=>$v){
            if($row['smart']==$k){
                echo "<option value='".$k."' selected>".$v."</option>";
            } else {
                echo "<option value='".$k."'>".$v."</option>";
            }
        }
    echo "</select></td>";
    echo "</tr>";
    echo "</table>";
    echo "</div>";


foreach(배열 as $key => $value){


}


여기서 $key 는 배열의 나열순서 0, 1을 의미하며, 배열의 크기가 5라면 0,1,2,3,4

DB에서 가져온 $row['smart'] 값과 key값을 비교하여 같으면 해당 value 를 출력하라는 의미다.

블로그 이미지

Link2Me

,
728x90

새로운 기능을 테스트할 때마다 Android Studio 앱 gradle 설정시마다 세팅정보 맞추는데 힘들어서 적어둔다.


android {
    compileSdkVersion 23
    buildToolsVersion "25.0.0"

    defaultConfig {
        applicationId "com.tistory.link2me.carousel"
        minSdkVersion 19
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:23.2.1'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    compile 'com.squareup.okhttp3:okhttp:3.9.0' // 서버와의 통신 라이브러리
    compile 'com.squareup.picasso:picasso:2.5.2' // 이미지 라이브러리
    compile 'com.android.support:recyclerview-v7:23.2.1' // ListView 개선 버전
    compile "com.android.support:cardview-v7:23.2.1"


너무 최신버전으로 컴파일 하지 않으려고 하다보니 버전에 맞는걸 찾아서 적어줘야 하더라.

dependencies 에 추가되는 라이브러리 버전이 다르니까 밑줄이 그어지면서 수정하라고 나온다.


테스트하는 어플 기능이 이 라이브러리를 모두 필요로 하는 것은 아니지만 기본적으로 추가해두고 테스트를 하고 있다.


Android Studio 를 3.0 으로 업그레이드 했더니

buildToolsVersion "26.0.2" 으로 변경하라고 나온다.

buildToolsVersion "25.0.0" 은 에러가 발생하면서 계속 수정하라는 에러를 보여준다.


 dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:23.2.1'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    implementation 'com.synnapps:carouselview:0.0.9'
    implementation 'com.squareup.picasso:picasso:2.5.2'  // 이미지 라이브러리
    implementation 'com.android.support:recyclerview-v7:23.2.1' // ListView 개선 버전
    implementation 'com.android.support:cardview-v7:23.2.1'
}


Android Studio 3.0 에서는 dependencies 에 compile 대신 implementation 으로 변경되었나 보다.




블로그 이미지

Link2Me

,
728x90

네이버지식인에 자바스크립트 값을 PHP로 넘기는 것에 대해 올라와서 의구심이 들었다.

이런 건 해본적이 없는거 같기도 하고 요즈음 안드로이드 배운다고 jQuery 배우다 중단해서 많이 잊어버려 기억도 안나고 해서 테스트를 해봤다.


<?php
include_once 'dbconnect.php'; // db접속
$width = '<script> document.write(window.screen.width); </script>';
echo 'width : '.$width.'<br />'; // 화면으로 보이는 건 숫자로 보인다. 소스보기로 하면 숫자가 아니다.
echo gettype ($width) . "<br />"; // 타입검사를 하면 string 으로 나온다.
$price = intval($width);  // 정수형으로 변환을 하면 숫자로 나올까??

$sql = "SELECT * FROM table_items WHERE Price > {$price} ";
echo $sql.'<br />';
$result=mysqli_query($dbconn,$sql);
while($row=mysqli_fetch_row($result)){
    echo $row[1].' '.$row[2];
    echo '<br />';
}

?>


$width ="1440";
echo intval($width);
와 같이 직접 입력한 String은 정수형으로 형변환(casting)을 잘 한다.


하지만 자바스크립트에서 받은 값을 직접 DB에 넣어봤더니 숫자로 인식하지 못한다.

육안으로 보이는 <script> document.write(window.screen.width); </script> 는 실제 숫자가 아니라 형변환(intval(string))을 하면 0을 반환한다.


좀 더 명확하게 하기 위해서

include_once 'dbconnect.php'; // db접속
$width = '<script> document.write(window.screen.width); </script>';
echo 'width : '.$width.'<br />';
echo gettype ($width) . "<br />"; //Returns string
$price = intval($width);

$sql = "SELECT * FROM table_items WHERE Price > {$width} ";
echo $sql.'<br />';
$sql = "SELECT * FROM table_items WHERE Price > {$price} ";
echo $sql.'<br />';
이렇게 출력을 해봤다.

화면상으로는

SELECT * FROM table_items WHERE Price > 1920
SELECT * FROM table_items WHERE Price > 0

이렇게 보인다.


브라우저 소스보기로 보면

SELECT * FROM table_items WHERE Price > <script> document.write(window.screen.width); </script>
SELECT * FROM table_items WHERE Price > 0
로 보인다.


https://stackoverflow.com/questions/9715231/unable-to-convert-string-to-integer-in-php

에 PHP 와 JavaScript 언어의 차이점을 간략하게 설명하면서 잘못하고 있다는 걸 답변달아주고 있다.


서버 코드와 Client 코드를 혼용하는 것은 로직이 잘못된 거다.


예전에 "자바스크립트와 PHP 값 전달 이해" 라고 정리해둔 http://link2me.tistory.com/1124 를 보면 조금 도움될 수 있다.

아직 자바스크립트에 대해 완벽하게 이해를 한 것이 아니라서 기회가 될 때마다 수정보강할 예정이다.

블로그 이미지

Link2Me

,
728x90

자바 동영상 강좌를 들으면서 자바 개념을 학습중이다.

"intanceof 연산자가 이거다"라고 쏙 머리속에 들어오지 않지만 블로깅하고 요약해서 적어둔다.


instanceof 연산자
- 프로그램 실행시 참조 데이터형을 검사하기 위해 사용되는 연산자이다.
- 형변환이 가능한지 묻는 연산자이다.
- 형변환이 가능하면 true, 가능하지 않으면 false를 반환한다.

A instanceof B
- A : Object
- B : Class or Interface
- B가 클래스일 경우에는 A가 반드시 B 클래스와 상속관계에 있어야먄 유효하다.
  B 클래스 상속관계 : A가 B클래스의 객체이거나 하위 클래스의 객체
  A와 B가 상속관계가 없을 경우 캐스팅이 불가능하기에 컴파일 타임에 에러를 발생시킨다.
- B가 인터페이스일 경우에는 컴파일 에러는 발생하지 않는다. 런타임 에러가 발생한다.

if(animal instanceof Dog){
    System.out.println("멍멍이 짖는 소리");
} else if(animal instance of Duck){
    System.out.println("오리가 우는 소리");
}


stackoverflow 에 나온 Answer 예제

public class Animal{ float age; }
public class Lion extends Animal { int claws;}
public class Jungle {
    public static void main(String args[]) {

        Animal animal1 = new Animal(); // B클래스
        Animal animal2 = new Lion();  // B클래스의 하위 객체
        Lion lion1 = new Lion();
        Animal animal3 = new Animal();
        Lion lion2 = new Animal();   //won't compile

        if(animal1 instanceof Lion)   //false
        if(animal2 instanceof Lion)   //true
        if(lion1 insanceof Lion)      //true
        if(animal3 instanceof Animal) //true
    }
}



'안드로이드 > Java 문법' 카테고리의 다른 글

Java BubbleSort  (0) 2019.05.13
java Singleton Design Pattern  (0) 2018.09.24
Java static 변수 이해하기  (0) 2017.10.20
[Java] 정보은닉과 캡슐화  (0) 2017.10.16
자바 기본 데이터형과 크기  (0) 2017.10.11
블로그 이미지

Link2Me

,
728x90

단순히 String값이 비어있는지를 체크하고싶은 경우는 TextUtils을 이용하면 좋다.
TextUtils.isEmpty(string)
TextUtils.isEmpty() 를 클릭해서 메서드를 확인해보면 아래와 같다.
public static boolean isEmpty(@Nullable CharSequence str) {
    if (str == null || str.length() == 0)
        return true;
    else
        return false;
}

// 사용법
if(TextUtils.isEmpty(string)){
    // 정말 string 이 비어있으면 true 를 반환
    doSomething();
}


EditText et;
et = (EditText) findViewById(R.id.et01);
// EditText 내용을 가져오기
String getEdit = et.getText().toString();
if(TextUtils.isEmpty(getEdit)) {
    Toast.makeText(MainActivity.this, "값을 입력하세요." Toast.LENGTH_SHORT).show();
}




블로그 이미지

Link2Me

,
728x90

static 변수에 대해 알아보자.


- 클래스의 변수나 메소드는 해당 클래스가 인스턴스화 되기 전에는 사용할 수 없다.

  하지만 static 변수나 메소드는 인스턴스의 생성과 상관없이(즉, 객체를 생성하지 않고도)

  바로 접근이 가능하고, 사용이 가능하다.

- public 으로 선언되면 누구나 어디서든 접근이 가능하다.

- JVM 은 실행과정에서 필요한 클래스의 정보를 메모리에 로딩한다.
  이 로딩(Loading) 시점에서 static 변수가 초기화된다.

- static 메소드는 해당하는 객체를 생성하지 않고도 해당 메소드를 호출하기 위한 것이다.


class AAA {
    static int num1 = 0;
    public AAA(){
        num1++;
        System.out.println("값="+ num1);
    }
}

class BBB {
    public static void main(String[] args){
        AAA aaa1 = new AAA();
        AAA aaa2 = new AAA();
        AAA aaa3 = new AAA();
    }


위 예제에서 static int num1 으로 선언된 변수는 Class AAA 에 종속될까? 안될까?

static 으로 선언된 변수는 Class AAA 에 포함되지 않는 변수다. 즉, 어떤 객체에도 포함되지 않으며 항상 값이 유지되도록 하는 변수이다.

그러므로 Class BBB에서 실행된 결과는 1, 2, 3 으로 증가된다.


값이 항상 고정적으로 이용해야 될 경우에는 final static 으로 선언해서 이용한다.

final static int MaxSpeed = 300; // 최대 스피드

final static int MinSpeed = 0; // 최저 스피드



Android 생명주기에서 onDestroy()가 호출되면 해당 Activity가 종료된다.

static 변수는 Android 생명주기와 상관없이, 해당 Process가 종료되어야 메모리에서 자유로워진다.

하지만, Android 에서는 어플의 Process를 가능한 한 유지하기 위해 노력하고 있다. 극단적인 메모리 부족현상이 발생하기 전에는 한번 실행된 어플의 Process 는 거의 죽지 않는다고 보면 된다.

View 처리를 하는 변수에 static을 잘못 사용하면 전혀 엉뚱한 결과나 오동작이될 수 있다는 것이 이해되는가?


Activity 생명주기(life cycle)와는 전혀 상관이 없고 Process가 종료되는 시점에 초기화된다.

Android 에서는 static 변수 사용은 정말 조심해야 한다는 걸 명심하자.


'안드로이드 > Java 문법' 카테고리의 다른 글

java Singleton Design Pattern  (0) 2018.09.24
[Java] instanceof 연산자  (0) 2017.10.24
[Java] 정보은닉과 캡슐화  (0) 2017.10.16
자바 기본 데이터형과 크기  (0) 2017.10.11
Java 클래스(Class) ★★★★★  (0) 2017.05.04
블로그 이미지

Link2Me

,
728x90

Android SearchView 에서 숫자검색, 초성검색, 자동검색 완성 기능 추가된 코드다.


   // Filter Class
    public void filter(String charText) {
        charText = charText.toLowerCase(Locale.getDefault());
        lvItemList.clear();
        if (charText.length() == 0) {
            lvItemList.addAll(addressItemList);
        } else {
            for (Address_Item wp : addressItemList) {
                if(Utils.isNumber(charText)){ // 숫자여부 체크
                    if(wp.getMobileNO().contains(charText) || wp.getOfficeNO().contains(charText)){
                        // 휴대폰번호 또는 사무실번호에 숫자가 포함되어 있으면
                        lvItemList.add(wp);
                    }
                } else {
                    String iniName = HangulUtils.getHangulInitialSound(wp.getUserNM(), charText);
                    if (iniName.indexOf(charText) >= 0) { // 초성검색어가 있으면 해당 데이터 리스트에 추가
                        lvItemList.add(wp);
                    } else if (wp.getUserNM().toLowerCase(Locale.getDefault()).contains(charText)) {
                        lvItemList.add(wp);
                    }
                }
            }
        }
        notifyDataSetChanged();
    }

 public class Utils {
    public static void CopyStream(InputStream is, OutputStream os)
    {
        final int buffer_size=1024;
        try
        {
            byte[] bytes=new byte[buffer_size];
            for(;;)
            {
                int count=is.read(bytes, 0, buffer_size);
                if(count==-1)
                    break;
                os.write(bytes, 0, count);
            }
        }
        catch(Exception ex){}
    }

    // 숫자인지 여부 체크
    public static boolean isNumber(String str){
        try {
            Double.parseDouble(str) ;
            return true;
        } catch(Exception e){
            return false;
        }
    }

}



블로그 이미지

Link2Me

,
728x90

SearchView 기능을 추가하고 났더니, SearchView 포커스가 자동으로 키 입력을 기다리고 있어서 영 불편하다.

activity_main.xml 파일에서 아래 빨간색으로 표시된 부분을 추가하면 자동으로 포커스가 잡히는 것이 해제된다.


 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <SearchView
        android:id="@+id/search"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:iconifiedByDefault="false"
        android:focusable="false"
        android:focusableInTouchMode="false">

        <requestFocus />
    </SearchView>

    <RelativeLayout
        android:id="@+id/list_view_relative2"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@color/whitegray" >

        <CheckBox
            android:id="@+id/lv_checkbox_all"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:text="전체선택"
            android:textColor="#000000"
            android:textStyle="bold" />

        <Button
            android:id="@+id/btn_cancle"
            android:layout_width="51dp"
            android:layout_height="33dp"
            android:layout_marginLeft="10dp"
            android:layout_centerVertical="true"
            android:background="@drawable/btn_send_cancel" />

        <Button
            android:id="@+id/btn_send"
            android:layout_width="51dp"
            android:layout_height="33dp"
            android:layout_centerVertical="true"
            android:layout_marginLeft="30dp"
            android:layout_toRightOf="@+id/btn_cancle"
            android:background="@drawable/btn_send_act" />

    </RelativeLayout>

    <ListView
        android:id="@+id/listview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_below="@+id/search" />

</LinearLayout>


블로그 이미지

Link2Me

,
728x90

http://link2me.tistory.com/1377 (Android SearchView) 에서 초성검색 기능을 추가했다.

지인에게 보여드렸더니 초성검색 기능도 제공되면 좋겠다고 해서 구글링으로 초성검색을 검색하여 추가해봤다.

http://milkissboy.tistory.com/32 에 나온 코드를 복사해서 Class 를 하나 추가했다.

그리고 http://link2me.tistory.com/1377 에서 수정한 부분만 여기에 기록해둔다.


HangulUtils.java


초성검색되는 코드는 위 참조한 블로그에 있는 것 중에서 main 메소드 부분만 제거하고 나머지는 그대로 이용


수정 코드

 초성검색 없는 코드

        // Filter Class
        public void filter(String charText) {
            charText = charText.toLowerCase(Locale.getDefault());
            lvItemList.clear();
            if (charText.length() == 0) {
                lvItemList.addAll(addressItemList);
            } else {
                for (Address_Item wp : addressItemList) {
                    if (wp.getUserNM().toLowerCase(Locale.getDefault()).contains(charText)) {
                        lvItemList.add(wp);
                    }
                }
            }
            notifyDataSetChanged();
        }

초성검색 추가 코드

        // Filter Class
        public void filter(String charText) {
            charText = charText.toLowerCase(Locale.getDefault());
            lvItemList.clear();
            if (charText.length() == 0) {
                lvItemList.addAll(addressItemList);
            } else {
                for (Address_Item wp : addressItemList) {
                    String iniName = HangulUtils.getHangulInitialSound(wp.getUserNM(), charText);
                    if (iniName.indexOf(charText) >= 0) { // 초성검색어가 있으면 해당 데이터 리스트에 추가
                        lvItemList.add(wp);
                    } else if (wp.getUserNM().toLowerCase(Locale.getDefault()).contains(charText)) {
                        lvItemList.add(wp);
                    }
                }
            }
            notifyDataSetChanged();
        } 


초성검색어 : ㄱㅂ, ㅇㅈ민, 이ㅈㅇ 등 검색어로 이용 가능


블로그 이미지

Link2Me

,
728x90

Android Studio SearchView 기능을 이용하면 검색처리가 좀 더 편하다.


검색어 자동완성같은 기능을 구현하기 위한 방법이라고 봐도 된다.

먼저, http://abhiandroid.com/ui/searchview 게시글을 다운로드 해서 본인 PC에서 직접 테스트한다.

다운로드 하려면 성명과 E-Mail 을 입력하고 나면 Dropbox 에 올려진 파일을 다운로드 할 수 있다.


아래 내용은 서버에 있는 자료를 ArrayList 에 저장하고 저장된 자료를 기반으로 SearchView 구현에 필요한 것만 발췌한 것이다.


앱 build.gradle 설정 내용 예시




MainActivity.java 주요 내용

변수선언

    ListView listView;
    private ArrayList<Address_Item> addressItemList = new ArrayList<>(); // 서버 원본 데이터 리스트
    private ArrayList<Address_Item> searchItemList = new ArrayList<>(); // 검색한 데이터 리스트
    ListViewAdapter listViewAdapter;
    SearchView editsearch; 

 public void onCreate(Bundle savedInstanceState)

        listView = (ListView) findViewById(R.id.listview);
        listViewAdapter = new ListViewAdapter(this, searchItemList);
        listView.setAdapter(listViewAdapter); // Binds the Adapter to the ListView
        listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);


        // Adapter에 추가 데이터를 저장하기 위한 ArrayList
        getServerData();

        // Locate the EditText in listview_main.xml
        editsearch = (SearchView) findViewById(R.id.search);
        editsearch.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                // 문자열 입력을 완료했을 때 문자열 반환
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                // 문자열이 변할 때마다 바로바로 문자열 반환
                String text = newText;
                listViewAdapter.filter(text);
                return false;
            }
        });

     private void getServerData() {
        settings = getSharedPreferences("settings", Activity.MODE_PRIVATE);
        Uri.Builder builder = new Uri.Builder()
                .appendQueryParameter("idx", settings.getString("idx",""));
        String postParams = builder.build().getEncodedQuery();
        new getJSONData().execute(Value.IPADDRESS + "/get_json.php",postParams);
    }


    class getJSONData extends AsyncTask<String, Void, String> {
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            // Create a progressdialog
            mProgressDialog = new ProgressDialog(MainActivity.this);
            mProgressDialog.setTitle("Personal Profile JSON Parse");
            mProgressDialog.setMessage("Loading...");
            mProgressDialog.setIndeterminate(false);
            mProgressDialog.show();  // Show progressdialog
        }

        @Override
        protected String doInBackground(String... params) {
            try {
                return PHPComm.getJson(params[0],params[1]);
            } catch (Exception e) {
                return new String("Exception: " + e.getMessage());
            }
        }

        protected void onPostExecute(String result){
            searchJSON=result;
            showList();
            mProgressDialog.dismiss();
        }
    }


    // 아이템 데이터 추가를 위한 메소드
    public void getServerDataList(String photo_image, String uid, String name, String mobileNO, String officeNO, boolean checkItem_flag) {
        Address_Item item = new Address_Item();
        item.setPhoto(photo_image);
        item.setIdx(uid);
        item.setUserNM(name);
        item.setMobileNO(mobileNO);
        item.setOfficeNO(officeNO);
        item.setCheckBoxState(checkItem_flag);

        addressItemList.add(item);
    }

    // 선택한 데이터 추가를 위한 메소드
    public void selectDataList(String photo_image, String uid, String name, String mobileNO, String officeNO, boolean checkItem_flag) {
        Address_Item item = new Address_Item();
        item.setPhoto(photo_image);
        item.setIdx(uid);
        item.setUserNM(name);
        item.setMobileNO(mobileNO);
        item.setOfficeNO(officeNO);
        item.setCheckBoxState(checkItem_flag);

        searchItemList.add(item);
    }

     // 서버 정보를 파싱하기 위한 변수 선언
    String searchJSON;
    private static final String TAG_RESULTS="result";
    private static final String TAG_UID = "uid"; // 서버 테이블의 실제 필드명
    private static final String TAG_NAME = "userNM";
    private static final String TAG_MobileNO ="mobileNO";
    private static final String TAG_OfficeNO ="telNO";
    private static final String TAG_Image = "photo"; // 이미지 필드
    JSONArray peoples = null;

    protected void showList() {
        try {
            JSONObject jsonObj = new JSONObject(searchJSON);
            peoples = jsonObj.getJSONArray(TAG_RESULTS);

            addressItemList.clear(); // 서버에서 가져온 데이터 초기화
            for(int i=0;i < peoples.length();i++){
                JSONObject c = peoples.getJSONObject(i);
                final String uid = c.getString(TAG_UID);
                final String name = c.getString(TAG_NAME);
                final String mobileNO = c.getString(TAG_MobileNO);
                final String officeNO = c.getString(TAG_OfficeNO);
                final String Photo_Image = c.getString(TAG_Image);

                // 서버에서 가져온 데이터 저장 (원본 보관용과 검색용 사용 목적)
                getServerDataList(Photo_Image, uid, name, mobileNO, officeNO,false);
                selectDataList(Photo_Image, uid, name, mobileNO, officeNO,false);
            }

            runOnUiThread(new Runnable() { // 화면에 반영하기 위하여 실시간 갱신한다.
                @Override
                public void run() {
                    // 갱신된 데이터 내역을 어댑터에 알려줌
                    listViewAdapter.notifyDataSetChanged();
                }
            });
        } catch (JSONException e) {
            e.printStackTrace();
        }

    }

 ListViewAdapter 주요 부분
    private class ListViewAdapter extends BaseAdapter {
        ImageLoader imageLoader;
        Context context;
        LayoutInflater inflater;
        private List<Address_Item> lvItemList = null;

        public ListViewAdapter(Context context, List<Address_Item> items) {
            this.context = context;
            imageLoader = new ImageLoader(context);
            lvItemList = items;
        }

        public class ViewHolder {
            LinearLayout child_layout;
            ImageView photo_Image;
            TextView tv_name;
            TextView tv_mobileNO;
            TextView tv_officeNO;
            ImageView child_btn;
            CheckBox checkbox;
        }

        @Override
        public int getCount() {
            return lvItemList.size();
        }

        @Override
        public Object getItem(int position) {
            return lvItemList.get(position);
        }

        // 지정한 위치(position)에 있는 데이터 리턴
        @Override
        public long getItemId(int position) {
            return position;
        }

        public View getView(final int position, View convertView, ViewGroup parent) {
            final ViewHolder viewHolder;
            final Context context = parent.getContext();
            final Integer index = Integer.valueOf(position);

            // 화면에 표시될 View
            if(convertView == null){
                viewHolder = new ViewHolder();

                LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                convertView = inflater.inflate(R.layout.address_item,parent,false);

                convertView.setBackgroundColor(0x00FFFFFF);
                convertView.invalidate();

                // 화면에 표시될 View 로부터 위젯에 대한 참조 획득
                viewHolder.photo_Image = (ImageView) convertView.findViewById(R.id.profile_Image);
                viewHolder.tv_name = (TextView) convertView.findViewById(R.id.child_name);
                viewHolder.tv_mobileNO = (TextView) convertView.findViewById(R.id.child_mobileNO);
                viewHolder.tv_officeNO = (TextView) convertView.findViewById(R.id.child_officeNO);
                viewHolder.child_btn = (ImageView) convertView.findViewById(R.id.child_Btn);
                viewHolder.checkbox = (CheckBox) convertView.findViewById(R.id.list_cell_checkbox);

                convertView.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }

            // PersonData 에서 position 에 위치한 데이터 참조 획득
            final Address_Item addressItem = lvItemList.get(position);

            if(addressItem.getPhoto().equals("")){
                final Bitmap Base_Profile = PHPComm.autoresize_decodeResource(getResources(), R.drawable.photo_base, 160);
                viewHolder.photo_Image.setImageBitmap(Base_Profile);
            } else {
                String photoURL = Value.IPADDRESS + "/photos/" + addressItem.getPhoto();
                imageLoader.DisplayImage(photoURL, viewHolder.photo_Image);
            }

            viewHolder.tv_name.setText(addressItem.getUserNM());
            viewHolder.tv_mobileNO.setText(PhoneNumberUtils.formatNumber(addressItem.getMobileNO()));
            viewHolder.tv_officeNO.setText(PhoneNumberUtils.formatNumber(addressItem.getOfficeNO()));

            convertView.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View view) {
                    isMSG = true;
                    relative2.setVisibility(View.VISIBLE);
                    listViewAdapter.notifyDataSetChanged();
                    return false;
                }
            });

            if (isMSG == false) {
                viewHolder.child_btn.setVisibility(View.VISIBLE);
                viewHolder.child_btn.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        builder.show();
                    }
                });
                viewHolder.checkbox.setVisibility(View.GONE);
                convertView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        Toast.makeText(getApplicationContext(), "상세보기를 눌렀습니다 ===" + addressItem.getIdx(), Toast.LENGTH_SHORT).show();
                    }
                });
            } else {
                if (isMSG == true) {
                    viewHolder.child_btn.setVisibility(View.GONE);
                    viewHolder.checkbox.setVisibility(View.VISIBLE);
                    viewHolder.checkbox.setTag(position); // This line is important.

                    viewHolder.checkbox.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            if(lvItemList.get(position).isCheckBoxState() == true){
                                lvItemList.get(position).setCheckBoxState(false);
                            } else {
                                lvItemList.get(position).setCheckBoxState(true);
                            }
                        }
                    });

                    convertView.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            if(viewHolder.checkbox.isChecked() == false){
                                viewHolder.checkbox.setChecked(true);
                                lvItemList.get(position).setCheckBoxState(true);
                            } else {
                                viewHolder.checkbox.setChecked(false);
                                lvItemList.get(position).setCheckBoxState(false);
                            }
                        }
                    });

                }
            }

            // 재사용 문제 해결
            if(lvItemList.get(position).isCheckBoxState() == true){
                viewHolder.checkbox.setChecked(true);
            } else {
                viewHolder.checkbox.setChecked(false);
            }

            return convertView;
        }

        // Filter Class
        public void filter(String charText) {
            charText = charText.toLowerCase(Locale.getDefault());
            lvItemList.clear();
            if (charText.length() == 0) {
                lvItemList.addAll(addressItemList);
            } else {
                for (Address_Item wp : addressItemList) {
                    if (wp.getUserNM().toLowerCase(Locale.getDefault()).contains(charText)) {
                        lvItemList.add(wp);
                    }
                }
            }
            notifyDataSetChanged();
        }

    }


foreach 문의 기본적인 내용은 http://link2me.tistory.com/1204 참조하면 된다.

블로그 이미지

Link2Me

,
728x90

Android Studio 샘플 예제를 받은 걸 가지고 패키지명 변경을 하는 방법이다.


먼저 폴더를 선택한 다음에 3번까지 하고 나면, 폴더가 펼쳐져서 보인다.



이제 변경하고 싶은 곳에 마우스로 선택한 다음, Shift + F6 키를 누르면 패키지명 변경할 수 있게 나온다.



패키지명을 입력하고 Refactor 를 해주면 Do Refactor 가 나온다. 눌러주면 변경된다.


다른 폴더명도 이렇게 변경해주면 된다.


만약 폴더가 모자라면 수동으로 직접 폴더명을 생성해주면 자동으로 인식된다.

그러고나서, 파일을 마우스로 Drag & Drop 으로 해당 폴더에 옮겨준다.


이렇게 하고 나면 AndroidManifest.xml 파일이 변경된 것을 확인할 수 있다.


그런데 자동으로 변경되지 않는 것이 있다.

앱 build.gradle 정보는 변경되지 않는다.

applicationId 값을 수작업으로 변경해줘야 한다.

이걸 안해주었더니 "Error: Activity class does not exist." 가 나와서 에러 메시지 찾아서 수정하느라고 생쑈를 조금 했다.


apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "25.0.0"

    defaultConfig {
        applicationId "com.tistory.link2me.searchview"
        minSdkVersion 15
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:23.1.1'
} 



블로그 이미지

Link2Me

,
728x90

정보은닉(Information Hiding)
- 객체가 실행되는 과정이나 속성을 외부로부터 감추는 것.
  즉, 인스턴스 변수에 바로 접근 하지 못하도록 Private 선언을 해주는 것이다.
- private : Class 내부에서만 접근 가능
- public : 어디서든 접근 가능
- default : 접근제어 지시자를 선언하지 않은 경우로 동일 패키지 내에서는 접근 가능
- protected : 상속관계에 놓여 있어도 접근을 허용


캡슐화(encapsulation)
- 캡슐화는 메소드와 변수들을 클래스 하나로 묶어버리는 것
- 객체 외부에서는 개체 내부 정보를 직접 접근하거나 조작할 수 없고, 외부에서 접근할 수 있도록 정의된 오퍼레이션을 통해서만 관련 데이터에 접근할 수 있다.(getter, setter)
- 객체들 간의 메시지를 주고 받을 때 각 객체의 세부 내용은 알 필요가 없으므로 인터페이스가 간결해지고, 객체간의 결합도가 낮아진다.



Eclipse 에서 getter, setter 를 자동으로 생성하는 기능을 제공하고 있다.

public class Person1 {
    // 자바의 기본 원칙은 클래스 1개당 1개의 파일
    public String name; // 필드, 데이터 멤버
    public String number; //
} 

public 을 private 로 둘다 변경하고 나서,

Alt + Shlift + S + R를 누르면 팝업된 창이 나온다.

Alt + A 를 누르거나 마우스로 둘다 선택한 다음에

Generate 를 해주면 자동으로 생성된다.

public class Person1 {
    private String name; // 필드, 데이터 멤버
    private String number; 
   
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getNumber() {
        return number;
    }
    public void setNumber(String number) {
        this.number = number;
    }
}


'안드로이드 > Java 문법' 카테고리의 다른 글

[Java] instanceof 연산자  (0) 2017.10.24
Java static 변수 이해하기  (0) 2017.10.20
자바 기본 데이터형과 크기  (0) 2017.10.11
Java 클래스(Class) ★★★★★  (0) 2017.05.04
ArrayList and HashMap  (0) 2017.03.06
블로그 이미지

Link2Me

,
728x90

RecyclerView는 롤리팝 버전에서 새로이 등장한 위젯으로 안드로이드 ListView의 장/단점을 보완했다.

RecyclerView

ListView

 수직 스크롤, 수평 스크롤 지원

 수직 스크롤 지원

 ViewHolder 패턴 강제 적용

 (재활용 View 를 관리해주는 Class)

 ViewHolder 패턴 선택 적용

 Animation 적용이 쉽다
 (ItemAnimation : Item 항목의 Animation 관리 Class)

 Animation 적용이 어렵다


Target API에 따라서 숫자가 변경되니 Google SupportLibrary 사이트에서 확인해야 한다.
지원 라이브러리에 대한 내용은 https://developer.android.com/topic/libraries/support-library/features.html#v7 를 참조하면 도움된다.


Audio Player 에서 음악파일을 ListView 에 보여주는 걸 RecyclerView 로 변경하는 걸 해보고 신경써서 살펴볼 것만  적어둔다.

- 체크박스 선택 기능 및 전체 선택/해제 기능

- 버튼을 길게 누르면 체크박스를 선택할 수 있는 기능 (다중 선택)


아래 기술된 사항은 체크박스가 들어간 RecylcerView 라고 보면 된다.

보통 ListView 예제를 찾아보면 체크박스 선택하는 기능이 추가된 것은 찾기가 어렵다. 체크박스 선택 기능이 들어가면 코드 구현시 주의해야 할 사항이 있다.

아래 코드에는 ListView, RecyclerView 모두 체크박스 처리 부분이 들어가 있는데, RecyclerView 를 완벽하게 활용하지 못하므로 코드가 매끄럽지 못할 수 있다.


가장 먼저 앱 build.gradle 을 다음과 같이 추가해줬다. 이건 어디까지나 참고사항이므로 꼭 이대로 할 필요는 없다.

compileSdkVersion 23 으로 변경해서 테스트를 했다. 버전이 너무 높다고 좋은게 아닌거 같아서다.


apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "25.0.0"

    defaultConfig {
        applicationId "com.tistory.link2me.recyclerview"
        minSdkVersion 19
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:23.4.0'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    compile 'com.squareup.okhttp3:okhttp:3.9.0' // 서버와의 통신 라이브러리
    compile 'com.squareup.picasso:picasso:2.5.2' // 이미지 라이브러리
    compile 'com.android.support:recyclerview-v7:24.2.0' // ListView 개선 버전
    compile "com.android.support:cardview-v7:24.2.0"
    compile "com.android.support:design:24.2.0" // snack bar
}


dependencies 부분은 필요한 부분만 추가하면 된다.

RecyclerView 부분은 compile 'com.android.support:recyclerview-v7:24.2.0' 만 추가하면 된다.



activity_main.xml 파일에서 변경된 부분

ListView 부분이 RecyclerView 위젯으로 변경하면 된다. (색깔로 표시한 부분)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <RelativeLayout
        android:id="@+id/list_view_relative2"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@color/whitegray" >

        <CheckBox
            android:id="@+id/lv_checkbox_all"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:text="전체선택"
            android:textColor="#000000"
            android:textStyle="bold" />

        <Button
            android:id="@+id/btn_cancle"
            android:layout_width="51dp"
            android:layout_height="33dp"
            android:layout_marginLeft="10dp"
            android:layout_centerVertical="true"
            android:background="@drawable/btn_send_cancel" />

        <Button
            android:id="@+id/btn_send"
            android:layout_width="51dp"
            android:layout_height="33dp"
            android:layout_centerVertical="true"
            android:layout_marginLeft="30dp"
            android:layout_toRightOf="@+id/btn_cancle"
            android:background="@drawable/btn_send_act" />

    </RelativeLayout>

    <ListView
        android:id="@+id/my_listView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <RelativeLayout
        android:id="@+id/list_view_relative2"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@color/whitegray" >

        <CheckBox
            android:id="@+id/lv_checkbox_all"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:text="전체선택"
            android:textColor="#000000"
            android:textStyle="bold" />

        <Button
            android:id="@+id/btn_cancle"
            android:layout_width="51dp"
            android:layout_height="33dp"
            android:layout_marginLeft="10dp"
            android:layout_centerVertical="true"
            android:background="@drawable/btn_send_cancel" />

        <Button
            android:id="@+id/btn_send"
            android:layout_width="51dp"
            android:layout_height="33dp"
            android:layout_centerVertical="true"
            android:layout_marginLeft="30dp"
            android:layout_toRightOf="@+id/btn_cancle"
            android:background="@drawable/btn_send_act" />

    </RelativeLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical"/>

</LinearLayout>


MainActivity.java 파일

변수 선언 부분

   private ListView audiolistView; // 리스트뷰
    private ArrayList<Song_Item> songsList = null; // 음악 전체 데이터 리스트
    private ArrayList<Song_Item> playList = new ArrayList<>(); // 선택한 음악 데이터 리스트
    private ListViewAdapter listViewAdapter = null; // 리스트뷰에 사용되는 ListViewAdapter

    RecyclerView audiolistView; // 리스트뷰 (ListView 대신 RecyclerView)
    private ArrayList<Song_Item> songsList = new ArrayList<>(); // 음악 전체 데이터 리스트
    private ArrayList<Song_Item> playList = new ArrayList<>(); // 선택한 음악 데이터 리스트
    RecyclerView.Adapter listViewAdapter; // ListViewAdapter 대신 RecyclerView.Adapter
    RecyclerView.LayoutManager layoutManager;


protected void onCreate(Bundle savedInstanceState) 부분

       // Adapter에 추가 데이터를 저장하기 위한 ArrayList
        songsList = new ArrayList<Song_Item>(); // ArrayList 생성
        audiolistView = (ListView) findViewById(R.id.my_listView);
        listViewAdapter = new ListViewAdapter(this); // Adapter 생성
        audiolistView.setAdapter(listViewAdapter); // 어댑터를 리스트뷰에 세팅
        audiolistView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);

         // Adapter에 추가 데이터를 저장하기 위한 ArrayList
        audiolistView = (RecyclerView) findViewById(R.id.recyclerview);
        audiolistView.setHasFixedSize(true);

        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        audiolistView.setLayoutManager(layoutManager);

        listViewAdapter = new ListViewAdapter(songsList,this); // Adapter 생성
        audiolistView.setAdapter(listViewAdapter); // 어댑터를 리스트뷰에 세팅


체크박스 전체 선택/취소 버튼 부분

ListView 에서는 ListViewAdapter 부분에 전체 선택/해제 할 수 있는 기능을 넣어도 문제가 없었는데,

에러가 발생해서 public void setAllChecked(final boolean ischeked) 메소드 위치를 변경해서 해결했다.

        // all checkbox
        checkAll = (CheckBox) findViewById(R.id.lv_checkbox_all);
        checkAll.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (checkAll.isChecked() == true) {
                    isAll = true;
                    listViewAdapter.setAllChecked(checkAll.isChecked());
                    listViewAdapter.notifyDataSetChanged();
                } else {
                    isAll = false;
                    listViewAdapter.setAllChecked(checkAll.isChecked());
                    listViewAdapter.notifyDataSetChanged();
                }
            }
        });

        final Button calcel = (Button) findViewById(R.id.btn_cancle);
        calcel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                isMSG = false;
                relative2.setVisibility(View.GONE);
                listViewAdapter.setAllChecked(false);
                checkAll.setChecked(false); // 전체 선택 체크박스 해제
                listViewAdapter.notifyDataSetChanged();
            }
        });

         // all checkbox
        checkAll = (CheckBox) findViewById(R.id.lv_checkbox_all);
        checkAll.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (checkAll.isChecked() == true) {
                    isAll = true;
                    setAllChecked(checkAll.isChecked());
                    listViewAdapter.notifyDataSetChanged();
                } else {
                    isAll = false;
                    setAllChecked(checkAll.isChecked());
                    listViewAdapter.notifyDataSetChanged();
                }
            }
        });

        final Button calcel = (Button) findViewById(R.id.btn_cancle);
        calcel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                isMSG = false;
                relative2.setVisibility(View.GONE);
                setAllChecked(false);
                checkAll.setChecked(false); // 전체 선택 체크박스 해제
                listViewAdapter.notifyDataSetChanged();
            }
        });


음악데이터 추가 메소드도 위치를 변경해서 해결했다.


ListView 기반 ViewHolder 와 ListViewAdapter 부분

    class ViewHolder {
        public ImageView mImgAlbumArt;
        public TextView mTitle;
        public TextView mSubTitle;
        public TextView mDuration;
        public CheckBox checkbox;
    }

    private class ListViewAdapter extends BaseAdapter {
        Context context;
        public final Uri artworkUri = Uri.parse("content://media/external/audio/albumart");

        public ListViewAdapter(Context context) {
            this.context = context;
        }

        // 음악 데이터 추가를 위한 메소드
        public void addItem(long mId, long AlbumId, String Title, String Artist, String Album, Integer Duration, String DataPath, boolean checkItem_flag) {
            Song_Item item = new Song_Item();
            item.setmId(mId);
            item.setAlbumId(AlbumId);
            item.setTitle(Title);
            item.setArtist(Artist);
            item.setAlbum(Album);
            item.setDuration(Duration);
            item.setDataPath(DataPath);
            item.setCheckBoxState(checkItem_flag);
            songsList.add(item);
        }

        // CheckBox를 모두 선택하는 메서드
        public void setAllChecked(final boolean ischeked) {
            final int tempSize = songsList.size();
            if (isAll == true) {
                for (int i = 0; i < tempSize; i++) {
                    songsList.get(i).setCheckBoxState(true);
                }
            } else {
                for (int i = 0; i < tempSize; i++) {
                    songsList.get(i).setCheckBoxState(false);
                }
            }
        }

        @Override
        public int getCount() {
            return songsList.size(); // 데이터 개수 리턴
        }

        @Override
        public Object getItem(int position) {
            return songsList.get(position);
        }

        // 지정한 위치(position)에 있는 데이터 리턴
        @Override
        public long getItemId(int position) {
            return position;
        }

        // position에 위치한 데이터를 화면에 출력하는데 사용될 View를 리턴
        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            final ViewHolder viewHolder;
            final Context context = parent.getContext();
            final Integer index = Integer.valueOf(position);

            // 화면에 표시될 View
            if (convertView == null) {
                viewHolder = new ViewHolder();

                LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                convertView = inflater.inflate(R.layout.song_item, parent, false);

                convertView.setBackgroundColor(0x00FFFFFF);
                convertView.invalidate();

                // 화면에 표시될 View 로부터 위젯에 대한 참조 획득
                viewHolder.mImgAlbumArt = (ImageView) convertView.findViewById(R.id.album_Image);
                viewHolder.mTitle = (TextView) convertView.findViewById(R.id.txt_title);
                viewHolder.mSubTitle = (TextView) convertView.findViewById(R.id.txt_subtitle);
                viewHolder.mDuration = (TextView) convertView.findViewById(R.id.txt_duration);
                viewHolder.checkbox = (CheckBox) convertView.findViewById(R.id.list_cell_checkbox);

                convertView.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }

            // Song_Item 에서 position 에 위치한 데이터 참조 획득
            final Song_Item songItem = songsList.get(position);

            // 아이템 내 각 위젯에 데이터 반영
            Uri albumArtUri = ContentUris.withAppendedId(artworkUri, songItem.getAlbumId());
            Picasso.with(convertView.getContext()).load(albumArtUri).error(R.drawable.ic_launcher).into(viewHolder.mImgAlbumArt);
            viewHolder.mTitle.setText(songItem.getTitle());
            viewHolder.mSubTitle.setText(songItem.getArtist());

            int dur = (int) songItem.getDuration();
            int hrs = (dur / 3600000);
            int mns = (dur / 60000) % 60000;
            int scs = dur % 60000 / 1000;
            String songTime = String.format("%02d:%02d:%02d", hrs, mns, scs);
            if (hrs == 0) {
                songTime = String.format("%02d:%02d", mns, scs);
            }
            viewHolder.mDuration.setText(songTime);

            if (isMSG == false) {
                viewHolder.checkbox.setVisibility(View.GONE);
                convertView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        playList.clear();
                        PlayList(songsList.get(position).getmId(), songsList.get(position).getAlbumId(), songsList.get(position).getTitle(), songsList.get(position).getArtist(),
                                songsList.get(position).getAlbum(), songsList.get(position).getDuration(), songsList.get(position).getDataPath());

                        System.out.println("position=" + position + " 노래제목 : " + songsList.get(position).getTitle());

                        Intent intent = new Intent(MainActivity.this, Player.class);
                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | intent.FLAG_ACTIVITY_CLEAR_TOP);
                        intent.putExtra("playList", playList); // 배열 데이터
                        startActivity(intent);
                    }
                });

                convertView.setOnLongClickListener(new View.OnLongClickListener() {
                    @Override
                    public boolean onLongClick(View view) {
                        isMSG = true;
                        relative2.setVisibility(View.VISIBLE);
                        listViewAdapter.notifyDataSetChanged();
                        return false;
                    }
                });

            } else {
                if (isMSG == true) {
                    viewHolder.checkbox.setVisibility(View.VISIBLE);
                    viewHolder.checkbox.setTag(position); // This line is important.

                    viewHolder.checkbox.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            if (songsList.get(position).isCheckBoxState() == true) {
                                songsList.get(position).setCheckBoxState(false);
                            } else {
                                songsList.get(position).setCheckBoxState(true);
                            }
                        }
                    });

                    convertView.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            if (viewHolder.checkbox.isChecked() == false) {
                                viewHolder.checkbox.setChecked(true);
                                songsList.get(position).setCheckBoxState(true);
                            } else {
                                viewHolder.checkbox.setChecked(false);
                                songsList.get(position).setCheckBoxState(false);
                            }
                        }
                    });

                }
            }

            // 재사용 문제 해결
            if (songsList.get(position).isCheckBoxState() == true) {
                viewHolder.checkbox.setChecked(true);
            } else {
                viewHolder.checkbox.setChecked(false);
            }

            return convertView;
        }

    }


RecyclerView 기반 ListViewAdapter 부분

    class ListViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
        Context mContext;
        private ArrayList<Song_Item> songsList;
        Song_Item songItem;
        int Position;

        public final Uri artworkUri = Uri.parse("content://media/external/audio/albumart");

        public ListViewAdapter(ArrayList<Song_Item> items,Context context) {
            songsList = items;
            mContext = context;
        }

        public class ViewHolder extends RecyclerView.ViewHolder  {
            public ImageView mImgAlbumArt;
            public TextView mTitle;
            public TextView mSubTitle;
            public TextView mDuration;
            public CheckBox checkbox;

            public ViewHolder(final View itemView) {
                super(itemView);
                // 화면에 표시될 View 로부터 위젯에 대한 참조 획득
                mImgAlbumArt = (ImageView) itemView.findViewById(R.id.album_Image);
                mTitle = (TextView) itemView.findViewById(R.id.txt_title);
                mSubTitle = (TextView) itemView.findViewById(R.id.txt_subtitle);
                mDuration = (TextView) itemView.findViewById(R.id.txt_duration);
                checkbox = (CheckBox) itemView.findViewById(R.id.list_cell_checkbox);

                itemView.setOnLongClickListener(new View.OnLongClickListener() {
                    @Override
                    public boolean onLongClick(View view) {
                        isMSG = true;
                        relative2.setVisibility(View.VISIBLE);
                        listViewAdapter.notifyDataSetChanged();
                        return false;
                    }
                });

                itemView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        Position = getAdapterPosition();
                        if (isMSG == false) {
                            playList.clear();
                            PlayList(songsList.get(Position).getmId(), songsList.get(Position).getAlbumId(), songsList.get(Position).getTitle(), songsList.get(Position).getArtist(),
                                    songsList.get(Position).getAlbum(), songsList.get(Position).getDuration(), songsList.get(Position).getDataPath());

                            System.out.println("position=" + Position + " 노래제목 : " + songsList.get(Position).getTitle());

                            Intent intent = new Intent(MainActivity.this, Player.class);
                            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | intent.FLAG_ACTIVITY_CLEAR_TOP);
                            intent.putExtra("playList", playList); // 배열 데이터
                            mContext.startActivity(intent);
                        } else {
                            //Toast.makeText(mContext, ""+Position, Toast.LENGTH_SHORT).show();
                            if (checkbox.isChecked() == false) {
                                checkbox.setChecked(true);
                                songsList.get(Position).setCheckBoxState(true);
                            } else {
                                checkbox.setChecked(false);
                                songsList.get(Position).setCheckBoxState(false);
                            }
                        }

                    }
                });

            }

            public void setAudioItem(Song_Item item, final int position) {
                songItem = item;
                // 아이템 내 각 위젯에 데이터 반영
                Uri albumArtUri = ContentUris.withAppendedId(artworkUri, songItem.getAlbumId());
                Picasso.with(mContext).load(albumArtUri).error(R.drawable.ic_launcher).into(mImgAlbumArt);
                mTitle.setText(songItem.getTitle());
                mSubTitle.setText(songItem.getArtist());

                int dur = (int) songItem.getDuration();
                int hrs = (dur / 3600000);
                int mns = (dur / 60000) % 60000;
                int scs = dur % 60000 / 1000;
                String songTime = String.format("%02d:%02d:%02d", hrs, mns, scs);
                if (hrs == 0) {
                    songTime = String.format("%02d:%02d", mns, scs);
                }
                mDuration.setText(songTime);

                if (isMSG == false) {
                    checkbox.setVisibility(View.GONE);
                } else {
                    if (isMSG == true) {
                        checkbox.setVisibility(View.VISIBLE);
                        checkbox.setTag(position); // This line is important.
                        checkbox.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                //Toast.makeText(mContext, ""+position, Toast.LENGTH_SHORT).show();
                                if (songsList.get(position).isCheckBoxState() == true) {
                                    songsList.get(position).setCheckBoxState(false);
                                } else {
                                    songsList.get(position).setCheckBoxState(true);
                                }
                            }
                        });
                    }
                }

                // 재사용 문제 해결
                if (songsList.get(position).isCheckBoxState() == true) {
                    checkbox.setChecked(true);
                } else {
                    checkbox.setChecked(false);
                }
            }

        }

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            // 새로운 뷰를 만든다.
            View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.song_item,parent,false);
            ViewHolder viewHolder = new ViewHolder(itemView);
            return viewHolder;
        }

        @Override
        public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
            // ListView의 getView 부분을 담당하는 메소드
            // Song_Item 에서 position 에 위치한 데이터 참조 획득
            songItem = songsList.get(position);
            ((ViewHolder) holder).setAudioItem(songItem,position);
        }

        @Override
        public int getItemCount() {
            return songsList.size(); // 데이터 개수 리턴
        }

    }


이런 부분을 수정해서 RecyclerView 기반으로 변경하는데 성공했다.

좀 더 다른 예제도 활용해서 테스트를 해보고 RecyclerView 의 장점(?)을 파악해보련다.

블로그 이미지

Link2Me

,
728x90

자바 기본 데이터형 크기에 대한 사항이다.

데이터형
예약어
비트수
범위
①논리형
boolean
8bit
(1바이트)
true, false
②문자형
char

16bit
유니코드
(2바이트)

수치로는 0 ~ 65535('\u0000'~'\uFFFF')

유니코드:영어,숫자-1바이트, 그외 다국어-2바이트

③수치형
(정수)
byte
8bit
(1바이트)
-128 ~ 127(-2의7제곱~2의7제곱-1)
개수:2의8제곱
④수치형
(정수)
short
16bit
(2바이트)
-32,768 ~ 32,767(-2의15제곱~2의15제곱-1)
개수:2의16제곱
⑤수치형
(정수)
int
32bit
(4바이트)
-2,147,483,648 ~ 2,147,483,647(-2의31제곱~2의31제곱-1)
개수:2의32제곱
⑥수치형
(정수)
long
64bit
(8바이트)
-9,223,372,036,854,775,808~9,223,372,036,854,775,807
(-2의63제곱~2의63제곱-1)
개수:2의64제곱
⑦수치형
(실수형)
float
32bit
(4바이트)
±3.40282347E+38, ±1.40239846E-45,
IEEE 754-1985표준
⑧수치형
(실수형)
double
64bit
(8바이트)
-1.79769313486231570E308~+1.79769313486231570E308

 



출처: http://aventure.tistory.com/59 [H.A.P.P.Y]

 

데이터형
예약어
비트수
범위
①논리형
boolean
8bit
(1바이트)
true, false
②문자형
char

16bit
유니코드
(2바이트)

수치로는 0 ~ 65535('\u0000'~'\uFFFF')

유니코드:영어,숫자-1바이트, 그외 다국어-2바이트

③수치형
(정수)
byte
8bit
(1바이트)
-128 ~ 127(-2의7제곱~2의7제곱-1)
개수:2의8제곱
④수치형
(정수)
short
16bit
(2바이트)
-32,768 ~ 32,767(-2의15제곱~2의15제곱-1)
개수:2의16제곱
⑤수치형
(정수)
int
32bit
(4바이트)
-2,147,483,648 ~ 2,147,483,647(-2의31제곱~2의31제곱-1)
개수:2의32제곱
⑥수치형
(정수)
long
64bit
(8바이트)
-9,223,372,036,854,775,808~9,223,372,036,854,775,807
(-2의63제곱~2의63제곱-1)
개수:2의64제곱
⑦수치형
(실수형)
float
32bit
(4바이트)
±3.40282347E+38, ±1.40239846E-45,
IEEE 754-1985표준
⑧수치형
(실수형)
double
64bit
(8바이트)
-1.79769313486231570E308~+1.79769313486231570E308

출처: http://aventure.tistory.com/59 [H.A.P.P.Y]

위 표에 대한 출처는 위 사이트고

아래 내용을 "난 Java를 공부한 적이 없다구요" 동영상 강좌 자료를 들으면서 기본 개념 잡는데 많은 도움이 될 거 같아서 안드로이드 개발 도움 관점으로 요약 정리하고 있다.

자바의 정석 책을 부분 부분 참조를 하다보니 이해를 못하고 넘어가는 경우가 많다. 결국 개발시간을 허비하는 결과로 이어지기 일쑤여서 기본기를 다지는 것이 중요하다는 느끼는데 막상 자바 혼자 공부하려면 쉽지 않더라.


자동형변환 규칙(Impict conversion)


명시적 형변환 :  자동형변환 발생시점 표시를 위해서, 규칙에 위배되지만 변환이 필요한 상황

- int num2 = (int) num1;

- logn num4 = (long) num3;

- 형 변환 나눗셈 예제 : (float)9/4; // 먼저 9가 형변환되어 9.0f 로 변경되고 4 가 4.0f로 변환되어 연산 수행

  . 피 연산자가 정수면 정수형 연산 수행

  . 피 연산자가 실수면 실수형 연산 수행, 단 % 연산제 제외.


비트 연산의 특징

- left shift 연산자 (<<) : 대상 필드의 값을 2진 비트로 바꾼 후에 왼쪽으로 특정 비트 수만큼 이동 시키고 빈자리는 0으로 채우는 연산자이다.
- right shift 연산자(>>) : 대상 필드의 값을 2진수로 바꾼 후 오른쪽으로 특정 비트 수만큼 이동시키고 빈자리는 양수일때 0, 음수일때는 1로 채우는 연산자이다.

- 왼쪽으로의 비트 열 이동은 2의 배수의 곱

  예) 2<<2  : 2 X 2의 제곱 = 2 X 4 = 8

       1<<5 :  1 X 2의 5제곱 = 1 X 32 = 32

- 오른쪽으로의 비트 열 이동은 2배 배수의 나눗셈

  예) 8 >> 1 ; 8 / 2 = 4 출력

       8 >> 2  ; 8 / 4 = 2 출력



'안드로이드 > Java 문법' 카테고리의 다른 글

Java static 변수 이해하기  (0) 2017.10.20
[Java] 정보은닉과 캡슐화  (0) 2017.10.16
Java 클래스(Class) ★★★★★  (0) 2017.05.04
ArrayList and HashMap  (0) 2017.03.06
Java foreach 문  (0) 2017.03.01
블로그 이미지

Link2Me

,
728x90

요일과 접속시간 설정에 따라 접속 허용/불허하는 간단한 로직이다.

공휴일 접속체크 이런 것은 적용되지 않았다.


<?php
$day = date('N');// 요일: 1(월) ~ 7(일)
$time = date('H');// 시간
$week = array("일", "월", "화", "수", "목", "금", "토");
$access = FALSE;// 접근 불가능

if ($day <= 5) {// 월~금
    if ($time >= 08 && $time < 18) {
        $access = TRUE;
    } else {
        $access = FALSE;
    }
} else { // 토, 일
    $access = FALSE;
}

if ($access === TRUE) {
    echo '오늘 '.date('Y-m-d').', '.$week[date('w')].'요일, 현재 시간 '.date('H:i:d').'<br />';
    echo "접속 허용 ";
} else {
    echo '오늘 '.date('Y-m-d').', '.$week[date('w')].'요일, 현재 시간 '.date('H:i:d').'<br />';
    echo "접속 차단";
}

?>

블로그 이미지

Link2Me

,
728x90

자주 사용해본 방식이 아니라서 자료 찾으려면 시간도 걸리고 해서 MySQLi 방식(Prepared Statements in MySQLi
)으로 된 예제를 적어둔다.


<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDB";

// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);

// Check connection
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}

// prepare and bind
$stmt = $conn->prepare("INSERT INTO member (firstname, lastname, email) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $firstname, $lastname, $email);

// set parameters and execute
$firstname = "John";
$lastname = "Doe";
$email = "john@example.com";
$stmt->execute();

$firstname = "Mary";
$lastname = "Moe";
$email = "mary@example.com";
$stmt->execute();

$firstname = "Julie";
$lastname = "Dooley";
$email = "julie@example.com";
$stmt->execute();

echo "New records created successfully";

$stmt->close();
$conn->close();
?>
 


$stmt->bind_result($cnt); // POD 방식에서는 에러 발생
$stmt->close(); //POD 방식에서는 에러 발생

출처 : https://www.w3schools.com/PhP/php_mysql_prepared_statements.asp

Prepared Statements in PDO 과 Prepared Statements in MySQLi

두 방식 예제 모두가 나와 있으므로 참고하면 도움된다.


위 예제로는 부족한 점이 있어서 추가로 예제를  만들어서 테스트를 하고 적어둔다.


테이블 만들기

CREATE TABLE IF NOT EXISTS `NOTICE` (
  `uid` int(11) NOT NULL AUTO_INCREMENT,
  `noticeContent` varchar(100) NOT NULL,
  `noticeName` varchar(20) NOT NULL,
  `noticeDate` datetime NOT NULL,
  PRIMARY KEY (`uid`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; 


<?php
@extract($_POST); // POST 전송으로 전달받은 값 처리
// $_POST['noticeContent']라고 쓰지 않고, $noticeContent 라고 써도 인식하게 함

// 테스트 목적으로 직접 값을 입력하여 테스트 시도
$noticeContent="공지사항 등록 테스트";
$noticeName="관리자";
$noticeDate=date("Y-m-d H:i:s");

// DB 접속 테스트
$servername = "localhost";
$username = "root";
$password = "autoset";
$dbname = "address";

// Create connection
$con = new mysqli($servername, $username, $password, $dbname);

// Check connection
if ($con->connect_error) {
    die("Connection failed: " . $con->connect_error);
}

// DB에 자료가 있는지 검사
$stmt = $con->prepare("SELECT count(*) FROM NOTICE WHERE noticeContent=? and noticeName=?");
$stmt->bind_param("ss", $noticeContent, $noticeName);
$stmt->execute(); // execute query
$stmt->bind_result($cnt); // bind result variables
if($stmt->fetch()){
    //echo $cnt;
    if($cnt == 0){ // 중복 자료가 없으면
        $stmt->close(); // close statement
        $stmt = $con->prepare("INSERT INTO NOTICE(noticeContent, noticeName, noticeDate) VALUES(?, ?, ?)");
        $stmt->bind_param("sss", $noticeContent, $noticeName, $noticeDate);
        $stmt->execute();
        
        $response = array();
        $response["success"] = true;
        echo json_encode($response);
        $stmt->close(); // close statement
    } else {
        $response = array();
        $response["success"] = false;
        echo json_encode($response);
    }
}
$con->close();
?>


types
일치하는 bind 변수의 type을 가리키는 문자열
i : integer
d : double
s : string
b : blob

블로그 이미지

Link2Me

,
728x90

Android Studio 에서 모듈을 새로 추가하는 순서를 이미지로 적어본다.




Application Name 을 적는다.





여기까지 하고 나면 관련 파일이 생성된다.




여기까지 했으면 이제 build.gradle 파일을 열어서 수정한다.




위 그림처럼 dependencies 에 추가하고 싶은 라이브러리를 추가해준다.

여기서 compileSdkVersion 정보는 수정하지 않아도 되고 필요한 버전으로 수정하면 된다.



마지막으로

를 눌러준다.


혹시라도 이런 메시지가 나오면 ....

AndroidManifest.xml 파일을 수정해준다.

android:roundIcon="@mipmap/ic_launcher_round"

이 한줄을 삭제하고 저장하면 다시 Sync Now 를 눌러주면 메시지가 없어진다.


이렇게 하면 모듈 추가가 완성된 것이라고 보면 된다.


compile 'org.jsoup:jsoup:1.10.3' 를 추가한 이유는 Web Site 파싱 관련 라이브러리 이기 때문이다.

https://jsoup.org/download 에 가면 최신버전 정보가 나온다.

Web Site 파싱 연습을 해보고자 한다.

가장 먼저 내 홈페이지 로그인 기능을 해보려고 한다.

블로그 이미지

Link2Me

,