Android 액티비티를 팝업(Popup)으로 띄우기, 데이터 주고받기

반응형

MainActivity.java

 

public class MainActivity extends AppCompatActivity {

 

    TextView txtResult;

 

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

 

        txtResult = (TextView)findViewById(R.id.txtResult);

    }

 

    //버튼

    public void mOnPopupClick(View v){

        //데이터 담아서 팝업(액티비티) 호출

        Intent intent = new Intent(this, PopupActivity.class);

        intent.putExtra("data""Test Popup");

        startActivityForResult(intent, 1);

    }

 

    @Override

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        if(requestCode==1){

            if(resultCode==RESULT_OK){

                //데이터 받기

                String result = data.getStringExtra("result");

                txtResult.setText(result);

            }

        }

    }

}

 

 

PopupActivity.java

 

public class PopupActivity extends Activity {

 

    TextView txtText;

 

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        //타이틀바 없애기

        requestWindowFeature(Window.FEATURE_NO_TITLE);

        setContentView(R.layout.popup_activity);

 

        //UI 객체생성

        txtText = (TextView)findViewById(R.id.txtText);

 

        //데이터 가져오기

        Intent intent = getIntent();

        String data = intent.getStringExtra("data");

        txtText.setText(data);

    }

 

    //확인 버튼 클릭

    public void mOnClose(View v){

        //데이터 전달하기

        Intent intent = new Intent();

        intent.putExtra("result""Close Popup");

        setResult(RESULT_OK, intent);

 

        //액티비티(팝업) 닫기

        finish();

    }

 

    @Override

    public boolean onTouchEvent(MotionEvent event) {

        //바깥레이어 클릭시 안닫히게

        if(event.getAction()==MotionEvent.ACTION_OUTSIDE){

            return false;

        }

        return true;

    }

 

    @Override

    public void onBackPressed() {

        //안드로이드 백버튼 막기

        return;

    }

}

* 10 : 타이틀바 없애기

* 17-18 : Intent 에서 data 가져오기

* 25-27 : Intent로 data 전달하기

* 34-40 : 바깥 레이어를 눌러도 닫히지 않게 하기

* 43-46 : 안드로이드 백버튼 막기

 

 

activity_main.xml

 

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:orientation="vertical"

    android:padding="16dp"

    android:layout_width="match_parent"

    android:layout_height="match_parent">

    <LinearLayout

        android:paddingBottom="8dp"

        android:orientation="horizontal"

        android:layout_width="match_parent"

        android:layout_height="wrap_content">

        <TextView

            android:text="액티비티를 팝업으로 띄우기"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content" />

    </LinearLayout>

 

    <LinearLayout

        android:paddingBottom="8dp"

        android:orientation="horizontal"

        android:gravity="center"

        android:layout_width="match_parent"

        android:layout_height="wrap_content">

        <Button

            android:text="팝업"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:onClick="mOnPopupClick"/>

    </LinearLayout>

 

    <LinearLayout

        android:paddingBottom="8dp"

        android:orientation="horizontal"

        android:gravity="center"

        android:layout_width="match_parent"

        android:layout_height="wrap_content">

        <TextView

            android:id="@+id/txtResult"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content" />

    </LinearLayout>

 

</LinearLayout>

 

 

popup_activity.xml

 

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:background="#eeeeee"

    android:orientation="vertical"

    android:layout_width="300dp"

    android:layout_height="wrap_content">

 

    <!-- 타이틀바 -->

    <LinearLayout

        android:orientation="horizontal"

        android:layout_width="match_parent"

        android:layout_height="wrap_content">

        <TextView

            android:text="Notice"

            android:textSize="20sp"

            android:textColor="#fff"

            android:gravity="center"

            android:background="#ff7a00"

            android:layout_width="match_parent"

            android:layout_height="53dp" />

    </LinearLayout>

    <!-- //end 타이틀바 -->

 

    <!-- Notice -->

    <LinearLayout

        android:padding="24dp"

        android:orientation="vertical"

        android:layout_width="match_parent"

        android:layout_height="wrap_content">

        <TextView

            android:id="@+id/txtText"

            android:textSize="15sp"

            android:textColor="#000"

            android:alpha="0.87"

            android:gravity="center"

            android:layout_marginBottom="3dp"

            android:layout_width="match_parent"

            android:layout_height="wrap_content" />

    </LinearLayout>

    <!-- Notice -->

 

    <View

        android:background="#66bdbdbd"

        android:layout_width="match_parent"

        android:layout_height="1dp" />

 

    <!-- 닫기 버튼 -->

    <LinearLayout

        android:orientation="horizontal"

        android:gravity="center"

        android:layout_width="match_parent"

        android:layout_height="wrap_content">

        <Button

            android:text="확인"

            android:textSize="15sp"

            android:textColor="#ff7a00"

            android:padding="16dp"

            android:gravity="center"

            android:background="#00000000"

            android:layout_width="match_parent"

            android:layout_height="53dp"

            android:onClick="mOnClose"/>

    </LinearLayout>

    <!--// 닫기 버튼 -->

</LinearLayout>

팝업의 가로세로 길이는 직접 정해줍니다

 

 

AndroidMenifest.xml

 

<!-- 팝업 Activity -->

        <activity

            android:name=".PopupActivity"

            android:theme="@android:style/Theme.Dialog" />

* Popup으로 띄울 Activity의 Theme를 바꿔줍니다

 

 

결과

* 팝업 호출(데이터 전달) -> 팝업에서 전달받은 데이터 보여줌 , 팝업 닫기(데이터 전달) -> 데이터 받아서 보여줌

 

 



출처: https://ghj1001020.tistory.com/9 [혁준 블로그]

 

 

 

반응형

댓글()

MySQL general 로그를 logrotate 로 관리하기

리눅스/MySQL|2019. 6. 4. 12:06
반응형

1. general 로그 설정

 

[root@sysdocu ~]# vi /etc/my.cnf

[mysqld]

general_log_file  = /usr/local/mysql/data/general.log

general_log  = 1

 

 

2. logrotate 설정

 

[root@sysdocu ~]# vi /etc/logrotate.d/mysql

/usr/local/mysql/data/general.log {

    daily

    rotate 1    // 사용중인것 말고 날짜로 된것 하나만 보관하도록 한다

    dateext

    missingok

    sharedscripts

    create 660 mysql mysql

    postrotate

        /usr/local/mysql/bin/mysqladmin -uroot -p'00000000' flush-logs

    endscript

}

 

[root@sysdocu ~]# chmod 640 /etc/logrotate.d/mysql

 

강제 실행 테스트

[root@sysdocu ~]# logrotate -f /etc/logrotate.d/mysql

 

 

3. 로그 압축 백업 설정

 

용량이 큰 general 로그를 압축하여 보관하도록 한다.

 

[root@sysdocu ~]# vi /etc/crontab

59 23 * * * root sh /root/general_log.sh

 

[root@sysdocu ~]# vi /root/general_log.sh

!#/bin/bash

 

find /data2/general_log/ -ctime +200 -print -exec rm -f {} \;

date=`date +%Y%m%d`

tar cvzf /data2/general_log/general_log_${date}.tar.gz /data1/general.log-${date}

 

exit 0

 

[root@sysdocu ~]# chmod 700 /root/general_log.sh

 

반응형

댓글()

윈도우10 USB 부팅 디스크 만들기(우분투에서)

윈도우즈/OS 일반|2019. 6. 4. 09:35
반응형

우분투 리눅스에서 윈도우10 USB 부팅 디스크를 만드는 방법은 아래와 같습니다.

1. Window 10 ISO 다운로드

아래 링크에서 Window 10 ISO 파일을 다운로드 받습니다.

 

Windows 10 디스크 이미지 다운로드(ISO 파일)

www.microsoft.com

<Window 10 다운로드 화면>

2. WoeUSB 다운로드

우분투 터미널을 이용해 WoeUSB 설치합니다.

$ sudo add-apt-repository ppa:nilarimogard/webupd8 $ sudo apt update $ sudo apt install woeusb

<아이콘 모양>

3. USB 메모리 포맷

USB 부팅 디스크를 만들기 위해 USB 메모리를 포맷합니다. 포맷 종류(파일 시스템 종류)는 크게 '모든 시스템과 장치에 호환(FAT)', '대부분의 시스템과 호환(NTFS)', '리눅스 시스템과 호환(EXT4)', '암호화, 리눅스 시스템과 호환(LUKS + EXT4)' 4가지로 분류되는데 EXT4, LUKS+EXT4 를 제외한 나머지는 윈도우 파일 시스템으로 모든 또는 대부분의 시스템과 호환된다고 명시되어 있으나FAT 이나 NTFS 로 포맷을 했을 경우 부팅 디스크를 만드는 과정에서 오류가 발생할 수도 있습니다.

(화면 상에는 정상적으로 포맷된 것처럼 보이나 부팅 디스크를 만들 때 디바이스(USB)에 마운트된 모든 것들에 대한 언마운트 실행 오류가 발생합니다.)

그렇기 때문에 종류는 EXT4 또는 LUKS+EXT4 를 선택하여 포맷 진행합니다.

<USB 포맷>

<포맷 종류(EXT4)>

4. WoeUSB 로 부팅 디스크 설치

위 과정이 모두 진행되었다면 WoeUSB 를 실행하여 부팅 디스크를 설치합니다.

(Target device 는 USB 를 자동 인식합니다.)

<WoeUSB 부팅 디스크 설치>

 

 

[출처] https://m.blog.naver.com/PostView.nhn?blogId=sinjoker&logNo=221242863908&proxyReferer=https%3A%2F%2Fwww.google.com%2F

 

반응형

댓글()

haproxy on CentOS 7

리눅스/OS 일반|2019. 5. 29. 09:27
반응형

[root@sysdocu ~]# yum -y install haproxy

 

[root@sysdocu ~]# vi /etc/haproxy/haproxy.cfg

global
    log         127.0.0.1 local2
    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     4000
    user        haproxy
    group       haproxy
    daemon
    stats socket /var/lib/haproxy/stats

defaults
    mode                    http
    log                     global
    option                  httplog
    option                  dontlognull
    option http-server-close
    option forwardfor       except 127.0.0.0/8
    option                  redispatch
    retries                 3
    timeout http-request    10s
    timeout queue           1m
    timeout connect         10s
    timeout client          1m
    timeout server          1m
    timeout http-keep-alive 10s
    timeout check           10s
    maxconn                 3000

frontend  main *:80    // haproxy 구동시 사용할 포트를 지정
    acl url_static       path_beg       -i /static /images /javascript /stylesheets
    acl url_static       path_end       -i .jpg .gif .png .css .js

    use_backend static          if url_static
    default_backend             static    // 아래 backend 중에서 static 부분을 사용하겠다는 것. 여러개 미리 설정해놓고 선택하는 식

backend static
    balance     roundrobin    // roundrobin은 무조건 한번씩 번갈아 접속시키는 방식이다. source 값을 이용해도 된다.
    server      static 192.168.10.2:80 check    // 포워딩할 서버 IP 와 포트
    server      static 192.168.10.3:80 check    // 포워딩할 서버 IP 와 포트

backend app
    balance     roundrobin
    server  app1 127.0.0.1:5001 check
    server  app2 127.0.0.1:5002 check
    server  app3 127.0.0.1:5003 check
    server  app4 127.0.0.1:5004 check

 

[root@sysdocu ~]# systemctl enable haproxy

 

[root@sysdocu ~]# service haproxy start

 

반응형

댓글()

xml 파일로 말풍선 만들기

프로그래밍/Android (Java)|2019. 5. 27. 08:13
반응형

안드로이드에서 배경을 처리하기 위해서, 이미지나 컬러를 많이 사용한다.

그리고, res/drawable 폴더에 xml 파일을 만들어서 이미지처럼 사용할 수 도 있다.

 

이번 포스팅에서는 xml 파일 만을 이용하여, 아래와 같은 모양의 말풍선 layout 을 만들어보겠다.

 

 

화살표가 아래를 향하는 말풍선 구조는 이렇다.

 

<LinearLayout android:orientation="vertical"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_margin="10dip"

android:clickable="true" >

<FrameLayout android:src="@drawable/speech_box"

android:layout_width="match_parent"

android:layout_height="100dip">

</FrameLayout>

<ImageView android:src="@drawable/down_arrow_nor"

android:layout_width="30dip"

android:layout_height="30dip"

android:layout_gravity="center_horizontal" />

</LinearLayout>

 

그림으로 보면 이런 구조

 

좌우, 위로 향하는 화살표 말풍선은 잘 배치하면 된다.

말풍선 안에 단순 텍스트 뿐 아니라 이미지를 배치하거나 다른 레이아웃, 뷰를 쉽게 추가하기 위해서, 말풍선 바디 부분은 FrameLayout 을 사용했다.

 

사실 이 글에서 다루고 싶었던 내용은 삼각형 모양의 화살표를 xml 파일로 만드는 것이다.

알고보면 원리는 간단하다.

사각형을 그린 뒤, 45도 회전시킨 후, x, y 축으로 적당히 이동시켜서, 한쪽 모서리만 그려지게 만들면 된다.

 

아래로 향하는 화살표를 만들기 위해, drawable 폴더에 down_arrow_nor.xml 파일을 하나 만들어서 아래와 같이 사각형을 그려주도록 하자.

 

<?xml version="1.0" encoding="utf-8"?>

 

<item>

<shape android:shape="rectangle" >

<solid android:color="#88FFFFFF"/>

<padding 

android:left="0dip"

android:right="0dip"

android:top="0dip"

android:bottom="0dip"/>

</shape>

</item>

</layer-list>

 

레이아웃에 이미지뷰를 띄워서 확인해보면 아래 그림과 같이 사각형이 그려진다. (배경 컬러 : #555500)

 

이제 이 사각형을 오른쪽으로 45도 돌려주면 아래와 같이 8각형 모양으로 나타난다. 이는 내가 이미지뷰에 지정한 사이즈 (50dip x 50dip) 의 영역 밖에서 각 모서리 부분이 그려졌기 때문이다.

<?xml version="1.0" encoding="utf-8"?>

 

<item>

<rotate android:fromDegrees="45"

android:toDegrees="45">

<shape android:shape="rectangle" >

<solid android:color="#88FFFFFF"/>

<padding 

android:left="0dip"

android:right="0dip"

android:top="0dip"

android:bottom="0dip"/>

</shape>

</rotate>

</item>

</layer-list>

이제 모서리 부분만 보이도록 이미지를 X, Y 축 방향으로 적당히 이동하면 된다.

그런데 여기서 주의할 점은, 이미지 회전 시 X,Y 축도 같이 45도 틀어졌다는 것이다.

그래서, X 축 방향으로만, 혹은 Y 축 방향으로만 움직인다고 해서 모서리만 보이거나 하지 않는다. 두 축에 대해 모두 이동을 해야 한다.

 

여러번 해보면서 자신이 생각하는 모양에 맞게 맞춰나가는게 좋다.

내가 사용한 값은 이렇다.

<?xml version="1.0" encoding="utf-8"?>

 

<item>

<rotate

android:fromDegrees="45"

android:toDegrees="45"

android:pivotX="135%"

android:pivotY="13%" >

<shape android:shape="rectangle" >

<solid android:color="#88FFFFFF"/>

<padding 

android:left="0dip"

android:right="0dip"

android:top="0dip"

android:bottom="0dip"/>

</shape>

</rotate>

</item>

</layer-list>



출처: https://bbulog.tistory.com/19 [뿔로그]

 

 

 

반응형

댓글()

원격으로 윈도우 서버 패스워드 변경하기

윈도우즈/OS 일반|2019. 5. 23. 17:53
반응형

1. 다운로드

아래 URL 에서 유틸리티를 다운로드하고 압축을 풉니다.

https://docs.microsoft.com/ko-kr/sysinternals/downloads/pspasswd

 

PsPasswd v1.24  /  June 29, 2016

PSTools.zip
3.04MB

* 유틸리티를 실행한 테스트 환경은 Windows 2016 였으며,

   해당 유틸리티로 Windows 2012 R2, Windows 2019 Server 패스워드를 변경해 보았습니다.

   이후 Windows 버전이 나온다면 테스트 해보거나, 업데이트된 유틸리티를 받아야 합니다.

 

 

2. 실행

사용 예 : pspasswd \\서버IP -u 로그인계정 -p 패스워드 바꿀계정 바꿀패스워드

C:\PSTools> pspasswd \\192.168.10.2 -u administrator -p 1111 administrator 2222 /accepteula

PsPasswd v1.24 - Local and remote password changer
Copyright (C) 2003-2016 Mark Russinovich
Sysinternals - www.sysinternals.com

Password successfully changed.

 

 

* 처음에 명령 실행할때는 유틸리티 라이센스 확인 창이 뜨지만 (agree 누름) 그 이후부터는 뜨지 않습니다.

  또는 /accepteula 옵션으로 eula 라이센스에 대한 물음을 안받게 할 수도 있습니다.

 

 

반응형

댓글()

MSSQL sqlserver 오류 15404 windows nt 그룹/사용자 에 대한 정보를 가져올 수 없습니다. 오류 코드 sqlstate 42000 connlsloginsysadmin

윈도우즈/MSSQL|2019. 5. 20. 09:44
반응형

메시지

[298] SQLServer 오류: 15404, Windows NT 그룹/사용자 'hostname'\administrator'에 대한 정보를 가져올 수 없습니다.오류 코드 0x534. [SQLSTATE 42000] (ConnIsLoginSysAdmin)

   

hostname은 컴퓨터명입니다. 그리고 administrator는 서버 설치 후 계정명을 바꾸었다면 다르게 나올수도 있습니다.

   

머 일단 이런 메시지가 나오면 확인해봐야 할 사항은

   

Microsoft SQL Server Management Studio 실행하셔서 머 Sa계정이나 시스템계정으로 로그인 하시고

   

개체탐색기 왼쪽에 보면 보안 >> 로그인 하위의 계정들 중에 hostname\administrator가 현재 OS의 계정과

   

동일한지 확인해보세요.

   

아마 십중팔구는 동일하지 않을꺼에요. 그러면 서버 처음 설치했을때는 hostnanme\administrator를 현재 계정명으로

바꿔줘야 합니다. 멀로 바꾸냐구요?

   

개체탐색기의 데이터베이스 연결된 명을 확인해보세요

   

두번째로 서버역할의 sysadmin에도 바꾼 hostname\administrator가 있는지 확인하세요. 보통 로그인의 계정정보를

변경하면 자동으로 sysadmin의 정보도 바뀝니다.

   

혹시 없으면 추가해주세요

 

 

[출처] SQLServer 오류: 15404 발생원인 및 해결방법|작성자 스팀

 

 

 

반응형

댓글()

sql server 복제에서는 서버 연결 시 실제 서버 이름이 필요합니다.

윈도우즈/MSSQL|2019. 5. 17. 16:01
반응형

개발사에서 웹 에서 게임 DB 에 내용을 거의 실시간으로 보여주기 위해 복제 DB 를 설정하다가
복제 생성시 아래와 같은 오류가 발생한다고 Help 요청이 들어왔다.

 

===================================

xxxxxxxxxxxxxx에 연결할 수 없습니다.

===================================

SQL Server 복제에서는 서버 연결 시 실제 서버 이름이 필요합니다. 서버 별칭, IP 주소 또는 다른 대체 이름을 통한 연결은 지원되지 않습니다. 실제 서버 이름 'xxxxxxxxxxxxxx'을(를) 지정하십시오. (Replication.Utilities)

------------------------------
프로그램 위치:

   위치: Microsoft.SqlServer.Management.UI.ReplicationSqlConnection.CheckServerAlias(ServerConnection conn)
   위치: Microsoft.SqlServer.Management.UI.ReplicationSqlConnection.ValidateConnection(UIConnectionInfo connInfo, IServerType server)
   위치: Microsoft.SqlServer.Management.UI.ConnectionDlg.Connector.ConnectionThreadUser()

 

해당 문제는 컴퓨터 이름을 변경시에 발생한다.

----------------------------- 


이유인 즉 mssql2008을 설치하면 설치시에 서버의 HostName 이  저장된다.
그 후에 해당 서버에 HostName 이 변경 되면서  게시나 복제를 할려면 옛날 서버 이름으로 접근을 해서 위와 같은 오류가 발생한 것이다.

아래와 같이 기존 @@SERVERNAME 이름을 현재 HostName 으로 변경해 주면 된다.

USE master
GO

-- 예전 서버 이름을 확인한다.
SELECT @@SERVERNAME

-- 예전 서버 이름을 버린다.
EXEC sp_dropserver 'Old Server Name', 'droplogins'    // 예전 서버 이름 입력
GO

-- 새 서버 이름을 등록한다.
EXEC sp_addserver 'New Server Name', 'local'    // 새 서버 이름 입력
GO

 

-- 반드시 '서비스 재시작'을 해야 적용 된다.

 

 

[출처] http://egloos.zum.com/runhanii/v/2606562

반응형

댓글()

DialogFragment로 Custom Dialog 만들기

프로그래밍/Android (Java)|2019. 5. 17. 08:45
반응형

이번 포스팅에서는 안드로이드에서 기본 제공하는 다이얼로그가 아닌 커스텀 다이얼로그를 적용하면서 삽질 했던 내용에 대해서 써 보려고 한다.

만들고자 하는 모양은 아래와 같다. 이전에 포스팅한 라운드 및 그라데이션 처리된 나인패치 이미지가 적용된 다이얼로그이다.


하단의 스샷 처럼 라디오 버튼 [동의], [미동의] 선택값을 다이얼로그에 뿌려주는 것 까지 다루려고 한다. 하지만 목적은 커스텀 다이얼로그의 구현이고, 또 귀차니즘으로 인해 결과물의 폰트나 컬러, 버튼 등은 똑같이 구현 하지는 않으려고하니 많은 양해를 바란다. ㅠㅠ



참, 이 블로그의 포스팅 목적은 개발 초보인 본인의 삽질 방지를 위한 기록에 초점이 맞춰져 있으니, 잘못된 부분이 분명 있을 수 있고, 더 심플하고, 효율적이고 방법들이 얼마든지 있을 수 있다.그러한 부분들을 번거로움을 무릅쓰고 공유를 해 주신다면 감사히 받겠습니다.(편의상 반말로 쭉~ 갈 건데.. 왠지 여기서는 존댓말을 써야....)



여튼 이제 본론으로 들어가자.

다이얼로그를 커스텀하려니 막막함에 자연스레 검색을 하게 됐다. android custom dialog 라는 키워드로 구글에 검색을 해보면..

1. Dialog를 이용하는 방법
2. AlertDialog.Builder를 이용하는 방법
3. DialogFragment를 이용하는 방법

이렇게 3가지를 주로 찾을 수 있었다. 뭐야.. 뭐가 더 좋은거야...
검색을 계속 해보니 Android Developer 공식 홈에서 대화상자 페이지를 보니 뭘 써야 할지 알 수 있었다.

해당 내용은...

Dialog 클래스가 대화상자의 기본 클래스이지만, Dialog를 직접 인스턴스화하는 것은 삼가야 합니다. 대신 다음 서브클래스 중 하나를 사용하세요.

AlertDialog : 제목 하나, 최대 세 개의 버튼, 선택 가능한 품목 목록 또는 사용자 지정 레이아웃을 표시할 수 있는 대화상자입니다.DatePickerDialog 또는 TimePickerDialog : 미리 정의된 UI가 있는 대화상자로 사용자로 하여금 날짜 또는 시간을 선택할 수 있게 해줍니다.

이러한 클래스가 대화상자의 스타일과 구조를 정의하지만, 대화상자의 컨테이너로는 DialogFragment를 사용해야 합니다. DialogFragment 클래스는 대화상자를 만들고 그 외관을 관리하는 데 필요한 모든 컨트롤을 제공합니다. Dialog 객체에서 메서드를 호출하는 것 대신입니다.

대화상자를 관리하기 위해 DialogFragment를 사용하면 사용자가 Back 버튼을 누르거나 화면을 돌릴 때 등 수명 주기 이벤트를 올바르게 처리하도록 보장할 수 있습니다. DialogFragment 클래스를 사용하면 대화상자의 UI를 더 큰 UI에 포함시킬 수 있는 구성 요소로 재사용할 수도 있습니다. 이것은 기존의 Fragment와 똑같습니다(대화상자 UI를 크고 작은 화면에서 서로 다르게 나타나도록 하고자 하는 경우 등).


이렇게나 추천하니 써봐야겠다.
그래서 작성해 본 코드다.

- MainActivity에서 동의/미동의 선택에 따른 CustomDialogFragment를 실행하는 부분이다.

RadioGroup radioGroup = (RadioGroup)findViewById(R.id.radio_group);radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) { String agreeOrDisagree = null; if (checkedId == R.id.radio_group) { agreeOrDisagree = getString(R.string.radio_agree); } else { agreeOrDisagree = getString(R.string.radio_disagree); } CustomDialogFragment dialog = CustomDialogFragment.newInstance( getString(R.string.custom_dialog_msg, agreeOrDisagree) ); dialog.show(getSupportFragmentManager(), "dialog"); } });
CustomDialogFragment의 instance를 생성할때 resource의 string을 전달해줬는데, 
<string name="radio_agree">동의</string><string name="radio_disagree">미동의</string><string name="custom_dialog_msg"> 마케팅 정보 수신 %1$s 처리가 완료되었습니다. </string>
이런식으로 구성되어 있으며, %1$s 부분을 변수 처럼 getString을 통해 넘기는 인자값을 매핑해서 사용 할 수 있다. 만약 더 추가 적인 변수가 필요하다면, %1$s, %2$s, %3$s 이런식으로 string값에 넣어두고, 그 갯수에 맞게 인자값도 콤마로 구분지어서 던져 주면 된다.


- CustomDialogFragment 부분이다.

public class CustomDialogFragment extends DialogFragment implements View.OnClickListener{ private static final String TAG = "CustomDialogFragment"; private static final String ARG_DIALOG_MAIN_MSG = "dialog_main_msg"; private String mMainMsg; public static CustomDialogFragment newInstance(String mainMsg) { Bundle bundle = new Bundle(); bundle.putString(ARG_DIALOG_MAIN_MSG, mainMsg); CustomDialogFragment fragment = new CustomDialogFragment(); fragment.setArguments(bundle); return fragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getArguments() != null) { mMainMsg = getArguments().getString(ARG_DIALOG_MAIN_MSG); } } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); View view = getActivity().getLayoutInflater().inflate(R.layout.layout_custom_dialog, null); ((TextView)view.findViewById(R.id.dialog_confirm_msg)).setText(mMainMsg); view.findViewById(R.id.dialog_confirm_btn).setOnClickListener(this); builder.setView(view); return builder.create(); } private void dismissDialog() { this.dismiss(); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.dialog_confirm_btn: dismissDialog(); break; } } }
잉... 코드를 여기다 붙여 넣으니 원하는 만큼 잘 표시해 주지 못하는 것 같다. gist나 그런걸로 별도로 빼야 할 거 같다. 뭐여튼..

실행 시켜 보면 아래와 같이 보인다.



또 검색 검색.. 이것저것 해보면서 xml에서 속성이나 style도 건드려 봤지만, 자바 코드로 하는게 가장 확실한거 같다. 그래서 onCreateDialog에서 Dialog를 리턴하기 전에 아래와 같이 수정 해준다.

Dialog dialog = getDialog();dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));dialog.setCanceledOnTouchOutside(false);
setBackgroundDrawable로 전에 모서리 부분에 표시되던 기본 백그라운드를 투명으로 바꿔 표시되지 않게 했고, setCanceledOnTouchOutside는 말 그대로 다이얼로그 바깥부분 터치를 통한 닫기 여부의 설정이다.

그 뒤의 결과물은 아래와 같다.



다 됐다~ ..  됐나? 싶었다. 근데 좀 이상하다. width값이 내가 지정한대로 먹지 않는 듯한 느낌적인 느낌.
그래서 10dp를 때리고 실행 시켜봤더니, 역시나... width값이 먹질 않는다.ㅠㅠ

또 검색 검색.. 미천한 영어와 실력으로 인해 삽질삽질.... 그러다 발견한 .
1. onResume()에 width와 height를 resource의 dimes값을 자바 코드로 불러와 설정을 하고,
2. 다이얼로그의 layout을 RelativeLayout으로 설정한 뒤, width, height를 match_parent로 줘야한다.

라는 내용이었다. 그래서 작성한 onResume()과 그 결과다.

@Overridepublic void onResume() { super.onResume(); int dialogWidth = getResources().getDimensionPixelSize(R.dimen.dialog_fragment_width); int dialogHeight = ActionBar.LayoutParams.WRAP_CONTENT; getDialog().getWindow().setLayout(dialogWidth, dialogHeight);}



허허 잘된다.
매번 느끼지만 스택오버플로우 없으면 개발을 어찌하나 싶다..ㅋㅋ

끝.


* 추가적으로 onCreateDialog에서 AlertDialog.Builder를 사용하지 않고 onCreateView를 사용해서 같은 결과를 낼 수도 있다.

@Overridepublic View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.layout_custom_dialog, container, false); ((TextView)view.findViewById(R.id.dialog_confirm_msg)).setText(mMainMsg); view.findViewById(R.id.dialog_confirm_btn).setOnClickListener(this); Dialog dialog = getDialog(); dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); dialog.setCanceledOnTouchOutside(false); return view;}
onCreateDialog 부분을 지우고 위의 코드를 넣어도 잘 나온다. 무슨 차이가 있는지는 정확하게 파악은 못 했다.


* 해당 소스를 깃헙의 gist를 통해 올려놨고, 아래에 첨부하니 참고하시기 바란다.



<?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">

 

<LinearLayout

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical"

android:paddingLeft="20dp"

android:paddingRight="20dp"

android:paddingTop="30dp">

 

<LinearLayout

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:orientation="horizontal">

 

<TextView

android:id="@+id/terms_tv_sub_title"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_gravity="center_vertical"

android:text="서브타이틀 표시 영역"

android:textSize="12dp" />

 

<RadioGroup

android:id="@+id/radio_group"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_marginLeft="20dp"

android:orientation="horizontal"

android:textSize="12dp"

android:visibility="visible">

 

<RadioButton

android:id="@+id/radio_agree"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/radio_agree" />

 

<RadioButton

android:id="@+id/radio_disagree"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/radio_disagree" />

</RadioGroup>

</LinearLayout>

 

<TextView

android:id="@+id/terms_tv_detail_contents"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:layout_marginTop="30dp"

android:text="약관 전문 표시 영역"

android:textSize="12dp" />

</LinearLayout>

</RelativeLayout>

view rawactivity_main.xml hosted with ❤ by GitHub

public class CustomDialogFragment extends DialogFragment implements View.OnClickListener{

private static final String TAG = "CustomDialogFragment";

private static final String ARG_DIALOG_MAIN_MSG = "dialog_main_msg";

 

private String mMainMsg;

 

public static CustomDialogFragment newInstance(String mainMsg) {

Bundle bundle = new Bundle();

bundle.putString(ARG_DIALOG_MAIN_MSG, mainMsg);

 

CustomDialogFragment fragment = new CustomDialogFragment();

fragment.setArguments(bundle);

return fragment;

}

 

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

if (getArguments() != null) {

mMainMsg = getArguments().getString(ARG_DIALOG_MAIN_MSG);

}

}

 
 

@Override

public void onResume() {

super.onResume();

 

int dialogWidth = getResources().getDimensionPixelSize(R.dimen.dialog_fragment_width);

int dialogHeight = ActionBar.LayoutParams.WRAP_CONTENT;

getDialog().getWindow().setLayout(dialogWidth, dialogHeight);

}

 

@Override

public Dialog onCreateDialog(Bundle savedInstanceState) {

AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

View view = getActivity().getLayoutInflater().inflate(R.layout.layout_custom_dialog, null);

((TextView)view.findViewById(R.id.dialog_confirm_msg)).setText(mMainMsg);

view.findViewById(R.id.dialog_confirm_btn).setOnClickListener(this);

 

builder.setView(view);

Dialog dialog = builder.create();

dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));

dialog.setCanceledOnTouchOutside(false);

 

return dialog;

}

 

/*

@Override

public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {

View view = inflater.inflate(R.layout.layout_custom_dialog, container, false);

((TextView)view.findViewById(R.id.dialog_confirm_msg)).setText(mMainMsg);

view.findViewById(R.id.dialog_confirm_btn).setOnClickListener(this);

 

Dialog dialog = getDialog();

dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));

dialog.setCanceledOnTouchOutside(false);

 

return view;

}

*/

 

private void dismissDialog() {

this.dismiss();

}

 

@Override

public void onClick(View v) {

 

switch (v.getId()) {

case R.id.dialog_confirm_btn:

dismissDialog();

break;

}

}

}

view rawCustomDialogFragment.java hosted with ❤ by GitHub

<?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="@drawable/ninepatch_common_box_gray"

android:padding="20dp">

 

<TextView

android:id="@+id/dialog_confirm_msg"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_alignParentTop="true"

android:gravity="center_horizontal"

android:textSize="13sp"

android:textColor="#111111"

android:textStyle="bold" />

 

<Button

android:id="@+id/dialog_confirm_btn"

android:layout_width="66dp"

android:layout_height="wrap_content"

android:layout_below="@+id/dialog_confirm_msg"

android:layout_centerHorizontal="true"

android:layout_marginTop="15dp"

android:background="@null"

android:text="확인"

android:textSize="12dp" />

 

</RelativeLayout>

view rawlayout_custom_dialog.xml hosted with ❤ by GitHub

public class MainActivity extends AppCompatActivity {

 

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

 

RadioGroup radioGroup = (RadioGroup)findViewById(R.id.radio_group);

radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {

@Override

public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) {

String agreeOrDisagree = null;

 

if (checkedId == R.id.radio_group) {

agreeOrDisagree = getString(R.string.radio_agree);

} else {

agreeOrDisagree = getString(R.string.radio_disagree);

}

 

CustomDialogFragment dialog = CustomDialogFragment.newInstance(

getString(R.string.custom_dialog_msg, agreeOrDisagree)

);

dialog.show(getSupportFragmentManager(), "dialog");

}

});

}

}

view rawMainActivity.java hosted with ❤ by GitHub

공유

 

[출처] http://darrenkwon.blogspot.com/2017/04/dialogfragment-custom-dialog.html

반응형

댓글()

라디오버튼 사용하기

프로그래밍/Android (Java)|2019. 5. 17. 08:39
반응형

안드로이드 : 라디오버튼 RadioButton 예제1

라디오버튼(RadioButton) 들은 하나의 그룹으로 묶여서 그룹내에서는 오직 한개만 선택할수 있게 해야 한다.
체크박스와 달리 라디오 그룹은 RadioGroup 으로 그룹 전체를 등록한다

 RadioButton 은 각각 선언하는 것이 아니라 RadioGroup 으로 선언해서 사용한다.

 

 

activity_main.xml

http://schemas.android.com/apk/res/android"

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"

    android:orientation="vertical" >

 

    <TextView

        android:id="@+id/textView1"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="2015 최고의 걸그룹은?"

        android:textAppearance="?android:attr/textAppearanceLarge" />

 

    <RadioGroup

        android:id="@+id/radioGroup1"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content" >

 

        <RadioButton

            android:id="@+id/radio0"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:checked="true"

            android:text="소녀시대" />

 

        <RadioButton

            android:id="@+id/radio1"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:text="2NE1" />

 

        <RadioButton

            android:id="@+id/radio2"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:text="씨스타" />

 

        <RadioButton

            android:id="@+id/radio3"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:text="걸스데이" />

 

        <RadioButton

            android:id="@+id/radio4"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:text="에이핑크" />

 

        <RadioButton

            android:id="@+id/radio5"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:text="AOA" />

 

    

 

    <Button

        android:id="@+id/button1"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="선택완료" />

 

    <TextView

        android:id="@+id/textView2"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="결과창"

        android:textAppearance="?android:attr/textAppearanceLarge" />

 

MainActivity.java

public class MainActivity extends ActionBarActivity {

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

 

        // 체크박스와 달리 라디오 그룹은 그룹 전체를 등록한다

        // RadioButton 은 각각 선언하는 것이 아니라 RadioGroup 으로 선언해서 사용한다.

 

        final RadioGroup rg = (RadioGroup)findViewById(R.id.radioGroup1);

        Button b = (Button)findViewById(R.id.button1);

        final TextView tv = (TextView)findViewById(R.id.textView2);

        b.setOnClickListener(new View.OnClickListener() {

 

            @Override

            public void onClick(View v) {

                int id = rg.getCheckedRadioButtonId();

                //getCheckedRadioButtonId() 의 리턴값은 선택된 RadioButton 의 id 값.

                RadioButton rb = (RadioButton) findViewById(id);

                tv.setText("결과: " + rb.getText().toString());

            } // end onClick()

        });  // end Listener

 

    } // end onCreate

 

} // end Main

 

 

실행화면

 

 



출처: https://bitsoul.tistory.com/47 [Happy Programmer~]

 

 

 

 

반응형

댓글()

STUN 에 대한 설명

리눅스/OS 일반|2019. 5. 16. 18:01
반응형

STUN을 쓰려면 각 플레이어의 호스트는 공인 IP로 공개된 중개 호스트, Xbox 라이브나 플레이스테이션 네트워크 서버에 연결해야 한다. 중개 호스트는, 여러 호스트가 서로 직접 연결하기 위해 필요한 작업을 중개해 주는데, 이를테면 라우터 테이블에 항목을 개설하는 방법 등을 알려준다. 그 절차는 그림 2-22와 비슷한 순서로 진행되는데, 그림 2-23은 이 과정에서 오가는 패킷의 예제와 NAT 테이블의 모습을 보여준다. 우리 게임이 UDP 200번 포트에서 구동 중이라 게임 호스트 간 통신이 모두 200번 포트에서 이루어진다고 가정해 보자.

 

 

 

 

 

먼저 호스트 A는 게임 서버를 개설할 의도를 IP 4.6.5.10의 중개 서버(호스트 N)에 알리는 패킷을 보낸다. 패킷이 라우터 A를 거쳐 갈 때, 라우터 ANAT 테이블에 항목을 만들고 공인 IP를 발신자 주소로, 그리고 발신자 포트를 임의의 숫자 60000번으로 할당하여 재기입한다. 그다음 수정한 패킷을 호스트 N으로 보내는데, 호스트 N이 이걸 받으면 플레이어 A가 공인 IP 주소 18.19.20.21:60000의 호스트 A에 멀티플레이어 게임 서버를 띄워두었다는 사실을 기억해 둔다.

다음 호스트 B는 플레이어 A의 게임에 참가하고 싶다고 호스트 N에 알리는 패킷을 보낸다. 패킷이 라우터 B를 거쳐 갈 때, 라우터 B 역시 NAT 테이블을 갱신하고 패킷을 재기입하여 호스트 N에 보낸다. 호스트 N이 패킷을 받으면 공인 IP 12.12.6.5:62000의 호스트 B가 호스트 A에 접속하려 한다는 사실을 알게 된다.

이 시점에 호스트 N은 라우터 A를 거쳐 호스트 A에 전달하기 위한 공인 IP와 포트 정보를 알고 있는데, 이 정보를 호스트 B에 응답으로 보내면 호스트 B가 호스트 A에 직접 연결할 수 있을 것 같기도 하다. 하지만 앞서 언급한 바와 같이, 대부분 라우터는 패킷의 발신자를 검사하여 원래의 발신자가 아닌 경우에는 차단한다. 라우터 A는 이 포트를 통해 들어오는 패킷 중 오로지 호스트 N의 것만 통과시킨다. 호스트 B가 이 포트로 호스트 A에 접속하려 하면 라우터 A는 그 포트로 호스트 B와 통신한 적이 없으므로 패킷을 막아버린다.

다행히도 호스트 N은 라우터 B를 거쳐 호스트 B에 전달하기 위한 공인 IP와 포트 역시 알고 있다. 이 정보를 호스트 A에 보내면, 라우터 ANAT 테이블에 호스트 N이 있으므로 라우터 A는 이 패킷을 통과시킨다. 이제 호스트 A는 호스트 N이 공유해준 정보로 호스트 B에 패킷을 하나 보낸다. 이 부분이 좀 이상하게 여겨질 수 있다. 호스트 A는 게임 서버요 호스트 B는 클라이언트인데, 클라이언트가 서버에 접속하는 대신, 게임 서버가 클라이언트에 접속하다니 말이다. 게다가 라우터 B는 호스트 A를 아직 모르고 있으므로 패킷을 보내보았자 막혀버릴 것이 뻔하다. 쓸데없는 패킷을 왜 보내는 걸까. 이런 작업을 하는 이유는 바로 이 과정을 거치며 라우터 A가 테이블에 항목 하나를 만들기 때문이다.

패킷이 호스트 A를 출발해 호스트 B로 가는 동안 라우터 A를 거친다. 라우터 ANAT 테이블에는 이미 192.168.10.2:200 항목이 외부 포트 60000에 매핑되어 있다. 그래서 패킷을 내보낼 때 60000번 포트로 내보낸다. 그리고 그 항목에 추가로 호스트 B의 정보 12.12.6.5:62000 항목을 하나 더 매핑한다. 매핑 항목을 이렇게 추가하게 만드는 것이 이 기법의 핵심이다. 패킷은 비록 호스트 B에 도달하기 전 라우터 B에서 막혀버리지만, 이제 호스트 N이 호스트 B에게 18.19.20.21:60000으로 연결하라고 알려주어, 호스트 B가 그 주소로 패킷을 보내면 라우터 AB가 보낸 패킷을 순순히 통과시킨다. 앞서 호스트 B에게 보내면서 12.12.6.5:62000 항목을 기억해 둔 상태이기 때문이다. 라우터 A는 수신자를 192.168.10.2:200으로 재기입하여 호스트 A에 전달한다. 이제부터 호스트 AB는 중개 서버를 거치지 않고도 서로 공유한 공인 IP와 포트로 통신할 수 있다.



Note ≣

NAT에 관해 몇 가지 더 알아두어야 할 것이 있다. 첫째, 앞에서 설명한 기법이 모든 NAT에서 동작하는 건 아니다. 일부 NAT는 할당한 포트 번호를 계속 유지하지 않는 것도 있는데, 이를 대칭형 NAT(symmetric NAT)라 한다. 대칭형 NAT는 밖으로 나가는 요청마다 고유한 외부 포트를 할당한다. 해당 IP 주소와 포트가 이미 NAT 테이블에 있을 경우라도 그렇다. 이 때문에 STUN 메커니즘이 깨지는데, 라우터 A가 첫 패킷을 호스트 B에 보내려 할 때 새로운 외부 포트를 할당하기 때문이다. 호스트 N이 썼던 외부 포트로 호스트 B가 라우터 A와 접촉해 호스트 A에 접근하려 할 때, NAT 테이블에 해당 정보가 없으므로 패킷이 버려진다.

대칭형 NAT 중 보안이 강하지 않은 것도 있어 이들 NAT는 예측 가능한 순서로 외부 포트를 할당하는데, 이 점을 노려 포트 할당 예측(port assignment prediction)이라는 기법으로 STUN과 비슷한 트릭을 써서 대칭형 NAT를 투과하기도 한다. 보안이 강화된 NAT는 아예 임의로 포트를 할당하므로 쉽게 예측하기 어렵다.

STUNUDP에서만 동작한다3장 버클리 소켓에서 설명하겠지만, TCP는 포트 할당 체계가 다르고 접속을 리스닝하는 포트와 데이터를 주고받는 포트가 다르므로 다른 방법을 써야 한다. TCP 홀 펀칭(TCP hole punching)이라는 기법을 쓰면 이를 지원하는 NAT 라우터를 투과할 수 있다2.11 더 읽을거리 절의 RFC 5128에 이외에도 다양한 NAT 투과 기법을 소개하고 있으며, TCP 홀 펀칭도 찾아볼 수 있다.

마지막으로, NAT 라우터를 투과하는 데 많이 쓰는 방법이 또 있다. IGDP(Internet gateway device protocol)라는 방법으로, 일부 UPnP(Universal Plug and Play) 라우터가 채용하여 랜 호스트로 하여금 외부와 내부 포트 사이에 매핑을 수동으로 설정토록 하는 프로토콜이다. 항상 지원되는 것도 아니며 학술 가치도 덜하므로 굳이 여기서 다루진 않겠다. 구체적인 내용은 2.11 더 읽을거리 절을 참고하자.

 

반응형

댓글()